Spaces:
Sleeping
Sleeping
main
#1
by
IvanStudent
- opened
- .env +0 -5
- .gitattributes +35 -37
- .gitignore +0 -0
- Dockerfile +0 -0
- README.md +10 -13
- animations/laptopUser.json +0 -1
- animations/loginUser.json +0 -1
- app.py +0 -32
- catboost.pkl +0 -3
- dbpredictivesystemgenai.sql +0 -74
- df_articles.csv +0 -0
- gradientboosting.pkl +0 -3
- histgradientboosting.pkl +0 -3
- lightgbm.pkl +0 -3
- mlpregressor.pkl +0 -3
- paginas/__init__.py +0 -0
- paginas/conexionMysql.py +0 -24
- paginas/conexionTest.py +0 -1
- paginas/dashboard.py +0 -813
- paginas/dashboardDemo.py +0 -947
- paginas/demo.py +0 -27
- paginas/demokaleido.py +0 -5
- paginas/images/Logo dashboard.png +0 -3
- paginas/images/Logo general.png +0 -0
- paginas/images/Logo.png +0 -0
- paginas/login.py +0 -59
- paginas/userManagement.py +0 -32
- randomforest.pkl +0 -3
- regressionmodels/catboost.pkl +0 -3
- regressionmodels/gradientboosting.pkl +0 -3
- regressionmodels/histgradientboosting.pkl +0 -3
- regressionmodels/lightgbm.pkl +0 -3
- regressionmodels/mlpregressor.pkl +0 -3
- regressionmodels/randomforest.pkl +0 -3
- regressionmodels/xgboost.pkl +0 -3
- requirements.txt +0 -0
- xgboost.pkl +0 -3
.env
DELETED
@@ -1,5 +0,0 @@
|
|
1 |
-
DB_HOST=gondola.proxy.rlwy.net
|
2 |
-
DB_PORT=45741
|
3 |
-
DB_USER=root
|
4 |
-
DB_PASSWORD=OryNzMwGGmvoULIEGyBRgNLqolGdlsnw
|
5 |
-
DB_NAME=railway
|
|
|
|
|
|
|
|
|
|
|
|
.gitattributes
CHANGED
@@ -1,37 +1,35 @@
|
|
1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
-
images/Logo[[:space:]]dashboard.png filter=lfs diff=lfs merge=lfs -text
|
37 |
-
paginas/images/Logo[[:space:]]dashboard.png filter=lfs diff=lfs merge=lfs -text
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
.gitignore
DELETED
Binary file (41 Bytes)
|
|
Dockerfile
DELETED
Binary file (496 Bytes)
|
|
README.md
CHANGED
@@ -1,13 +1,10 @@
|
|
1 |
-
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
-
sdk:
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
---
|
12 |
-
|
13 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
+
---
|
2 |
+
title: PredictiveMLGenAI
|
3 |
+
emoji: 📈
|
4 |
+
colorFrom: yellow
|
5 |
+
colorTo: indigo
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
---
|
9 |
+
|
10 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
animations/laptopUser.json
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.20","a":"","k":"","d":"","tc":""},"fr":30,"ip":0,"op":120,"w":300,"h":225,"nm":"971 [Converted]g","ddd":1,"assets":[{"id":"image_0","w":65,"h":115,"u":"","p":"data:image/png;base64,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","e":1},{"id":"image_1","w":27,"h":17,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAARCAYAAAAsT9czAAABGklEQVRIS7WUwU3DQBREZxCCI+kAd0C4II50gKkAUwEpISWkA6cDoIPQAVSQ0AGcUBAwaNAPWgwRdlh/ydd9nnl/l5JKACcAxiQf0eNQ0gTAJYAnABOS4754hg0ATAGcBuQBwIjkdW4oVwdKcpVOeRApi9y1fsESaBUOnS6rwx+wZnWSDLdH+3TyjacNzNt6lfisSM42If4J86Hh00u0H5BbknbcaVrBEp+jqHQPwCHJuy60TrBI6atSknTSz5E0bAPuDFuzQDWA+7ifa33mgDmp61z5vAnoovlj/4Yl1dqnP/v00+dqvwGzwBJ3RSzQOYCz5pOXFfbbZmpYD5bb79Xu69a0d9jzcV3wTXNBF73DnHZ5VJc7L5h9AM+/YPcpkIXXAAAAAElFTkSuQmCC","e":1},{"id":"image_2","w":77,"h":65,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE0AAABBCAYAAACUyynLAAAIKklEQVR4XuVczXITRxDulsGWTzGXYJ9iniCmCqjcYp4g5pYCV0WGk8ghmyeIeALsQ9AJLKowlVvsJ4h9S2EO+AmCT4ZcQCdkG6tT32pGzI5W3pnRrn6creKAdnZ25tue7q+/6TFT4PXpu81FarcrVCo1Zv9eexvYzUQ+xqGjbt16tk1CP6jnN2ZOucZv1j6G9jdJzwWDdnxrc0VE/jQm2ySRWvn1g/VJAiBkrMGg4WWfbmwuc0kaJPSN8fIDEY5mX6/thgxoEp4ZCDQ9wdaNpxEx14joq+6kmXakxNFF9HfeoInIHBFhCS4RUcTMsUXJ0ubc8WUBcL+Y1sLMd2ZerW1PggW5jtEbtBggETh8bVU7Crw4gp7c3FxqkwDU79UgDsr79wHwhblCQYuI6LGFwjVm7lIPFShgebvl/ftof2GuINCUtcF6TIvaYOZMcERkkYgqRNQwQZ4kRINB05MUkRUiwr91Zn6TNXkRgQ/E0m0q0PHcRPG7gUHLAsm+LyKwsk3j90PlEycmWAwdNLW0ARz8ncnv9hR4mdbq+6Hybp8LaMp6dn18lKIu8IG/mcSYmcc+0g4Mmog0iOgn7aO4Q3KdLxUYEFB0HpuIws4dDbFhHqAtE9Ffg/ooEYGFvZ2EoDAwaEX6KJE4w8Dyhb+r+Cz/Ig0vF9AUcEivbB+FW8+Vg/emFSmRdgMBZNTWOBBo757ci4hpudSW2tc/v4yjXoqPws/gZJisl2ykggWoiE7JdF/IeeFLR3INBlr9HtKmmDYw0fPp1uXoyq+N2KJEBL4OIH1r+TssMy/ZSPUFkBISlCkYDBO9gUA7qq/WmMSkDE0Sqs0/3OpalFpi+P8X2Yjoukv2kEKMsfyTEhRRQjAYBngDgYYBHtVXl7mjaiQsSogrC9UXHdmoIyeZstFzZgbB9b4MaQo0R187zIxUbijXwKDpUSr/ZlkB7wl9rixU/4jVD+XvsGy3B3XmiqJ0BQNmzm0uWcgHvUgl6fAx8F9dH/XhcWXuuHxSI+KEECnEj8qtS+va32UNyuf+KPhdKGiQd/4xJpfgUUf1HxeZLjWIJBH1hCRaqL4sPOppX4vg1KazmrZ0n49xXtsg0NRSw9IwLarJzPBd3Uv5u56oJ8SR9nd5TcTs51397rph7U0hXl+ovvBK7woBzfBRAEVb1B1m7pF41JdH5DMj6I7QWZS3FXSCU6qlH7JwdPXhi4ElKG9Lg99qlU9Wyq3pbYuTzWnARAQAxTxNczI8d1I+XZdOcq+v2AqK8ndH9bsVpphQGx+L90rtdqTJeIile4P2rr66q3xVs5+PEhFkB5qCJHjUv7/fXWqXSuumv4PvuVrdCqIgWZPufOTPkcUnEcs3ZlrTtZDg5A9aJ3UyN1UObB+VkjP2SNvvn6yuCMf8Lmb589Ut77FkAZb0r1iyU6YEhds9ZNylz6CBuvgom0epwUDaRg4aR9CYosycVoT5TZGBISU4nUvGs4ALAk1P2MVHKU7XtSg1IFAUgOeVg2ZNxue+Cxnv158zaEYqFKdEWttK81FEdCgkNc3JDGnbjqDBspEPQP3ahpJxX9A+qAH0SD0qUiU2S5AJmPxIpVFoY0ZQp/3SPEDq14cvGXcGTfEyvR/QcaJEi2YOaUQqbVHN+epWgvCqfkzZ6JCZkWGM/HIl416gqQnr7Tf8F5OP80+UH2i5B1+uRFO1NsnueWkTdDLt1zoDjpUQMpP8USCZFei8QbMnIRLTBp1OBfuozhKZMvLZcB6VB9B9yPjhfHVr0Qk0VX8GbrZXIo6m99e6G7rKT5nJe8zJfLfyMNH39XsNK2OIB5kHCKF92IFOiG87gfbp5lOkI91SgljaPuVI19ieI21Dy/fK9Wy/UmrL9UFSnlCw7OdAxvEbclcn0ND4+OYz2wqaYPSzrx501YMUabsnWLhOAtEYbc0c1/XZots5g4aBxDW2ndTni7TNdMjEka52TCk3SFU+ip5Ykf1ngqZ81oq5/aaWq71ZsidTXNE1tuo50iRYlS/AxL238ooEIKRvF9B0qWiiJAo1tq3pdsTC5m4UxpB6psCoS0Mb9OW9lRcywSKecQHNJLQYwyMzMuLkCp/FRTA90vbs/oOutG3sKySESLNet4gJFtGnC2h2uUEqg085U9As799PZAMpW3l6To8UTfEuXSgClKw+M0HTHRjlBth+axi1sx9Nf9c9U8D0sSS8YnI6oy+7Xhe3EGlHWm6QBZa+3xc0I7nG1+8pOrGExgP7TIHLOak+stHaKOs0XIA7DzSz7iy1gMU6T4D3BZUIqJIqneQPdbfcBSS7DX+RdGTb1swtrR/PJi2qt9wg2Ecpf6dL5cfat7FSMLXmn1bAklZ0ktDA+kjbsE7QCq80KuTLD/sZ7qiXp0jAkyfpjA3dPlHvil2P0cdH9bQb9iTzfl/Xp6Vr5pTY0DUsCgUtXcnbHpTlo26Pci8gb8DQXyIQ9NGQKKSARVkn0ihd5IdlDp+F5Tpxp1RM8FOjZ5/NkiYLV0K39S2xcuJOqWSCphvYG7r4XejsWkj9heJ92LKbyFMqzqDphqZmLiRroeVSfU6p4DXBMnkRPiurT+c0Cho+OguxspRAgb56SwQmRDZyBi0L/ZD758jk0O/G9mDZSEEzEnhEVVPUPBjng2VjAVocYJIpGaIr9kTH8i/LjA1ohtVh1z4BllrG+L3wel0XNzN2oPUJGnpfNSEYuEywiDZjD5paumZlZUxRQg9v5AHipICm/4CKWW0UdFTofwOa4e8gjGJzGjzPe/c+D8B6Eva8Or3o/fwHC+kumM0DvnMAAAAASUVORK5CYII=","e":1},{"id":"image_3","w":135,"h":134,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIcAAACGCAYAAAD3u5mvAAAdCUlEQVR4Xu1de1xUVR7/DSTaiqZlKUqgtWapS0ZuUflAyV1NI3e3Ek2wfGBKKplSVpL4KA0U23ytj6KHpvg2DT5GLupaan20todorZtrJYMiCAMM87r7OXfmztx75557zzlzZxxk+Edn5j7OPed7vr/v73HONUAQ/bVokdjHwDlGoiYZHFy1wRG+ywylPwdRE5tVUwz+fNoZ3YriOM7es8FqOrHu15Qz6vdKbBdxnSNTfgzHcT9b7eG7AEqr/dnW0LW9e8Bv4JjSdVtB3MgO4/oO7Qp9/xwLZ09W1n9fcvn55Kw71ioNRIsWiQkGzjEUN0hhEFZqtsFRgFJzaCAD0wN+A8eY6PUNDoejFYABej4QBWnzH4DYnjfCdwfK//f9oeohKTl3SZikRXj/pw0GQ1fVx3aAmQsPK7ZaS78OTPc077v4BRzTu+3KqLBUrACOVw/OHuYAhk3qDY+/EM9//GrXbwcHjuuW6PwxsVXEdY6XSIeCc3Dl4Y7w4pAeIe0xtuP8Ao70mM0Hamw1g5xNct6C45z/tm4bwQNk2KReUPlbnePo5t8W/eWl9B2CEKV6DI4rs9jDi0N6hKrXiA/2Czg8JsUFDg4Rh+hWHECHWyNh6lsDoecDneDMlxWWrOfXbi069ulPxC0XDnSAOSws7GhIj1D3nOYJuoNj+m0fJ1U0XiiRmxQ3OHigeP56PhgFU98aADdHR8LuNd8Yc/JWbv3+3OlKzZbLDkCuryM8vDSkR2h7Dn+87uCYHFu484qteiQpODgXUp6YFQ/D03vxLf0g7/Oy6W+8toXlMZHrG24PLw3pEZbek56jOzhSo9+5bHXY2rv1hoJJEZiD/9f1Af3T+oYIeHp+AiSO6g7/O1Vl/8eS/UfyNq76J9NjctzXLj0Scn2ZOtDtSjCeLTsNBb2MlvPfELOGDBxO4QrQtfdNMH5BAvR8sBOc+OwX8/zsgu1Fx33RI6Wl+jxh87qKrsyR0XV7fqW1MpMYHCLWELpdMDPop/uGxcL4hQm8h7P/w1PG+cvY9QjnCC+2QGlZ8xpe355WV3CMu/W98432xmgWkyKwhhskrv/8rm0EjEjvDY9O7gX1tRaucPVXp59fwq5HQqF4csDoBo4Z3XZ3NFqM5cSsgTEpEnCIPJtbbo2EUVnxMGhUdyg7XmF9b+U/v1i2iU2PGCDsaKMNSkOheHWg6AaOjK47siutl3KIwaFhUvhmi8AhiNfeD0XBhEUJ0LXXjXB451nTjNmLC1hcX3CG4pHre5R8LjWvI3UDx4SYTV/W2Ux99TQpgv5wk4wIUINTusPERQn87Yo/PGUcPWP6GpahC5UG4HtNN3CkdFnLEbMGpUkRg0McQEN6JHlyb0jJugcu/mLi3nnz4Mns1W9+zAISCIXivbpNF3DwibbGihVu1hDlUuTmQSw8xQMt9lJwJkWEKScOXRe4OSYSJi1KgPuHxcKpYxXWl2auLSxmcX0BIFQa4MGILuBIj9lyoMZ2ZZA4AyvPpeACXzgvRc2k4M7p/WAUZK4cAEi8Fr9/quqlefkbT52nD8W79EizLw3QBRzi2g3PrPek6iUMIROaigMty78ILEHKNMnP9oIxLzpLA7avOnlu0tysAhZT09xLA3wGB59oM18ouVomxc1IIuQIofinsuIBAQXpkbw5+w4v38zm+oIzFI9c32ZVqugzOPhEm7V6pKJJkbmr8lyKniYFZ4ZuiYmE51cOhD881Am+KvnFvGBuwfbir3wJxTefUkWfwZHa5Z3LVg4l2qRFPXoIUZyXoileFUxXwiOxMPmNBF6PbFvxjXHhspVbWfRIcyoN8AkcfKKt8fw3rEJU0XNRCHzhvBScSVHzdp56KR5GTnGWBry7+POyF3LZQ/ER9vDiOigtZ9EzTeEcn8DBJ9oslZk4k6IkRCUDLdMJROKU8Bw1QKHSAMQiQ0Z3h59/qLKvfqP4yPLNa0KlATLE+gSOcdHvn290mKN5cKjUbTDHNlQ8GzdG5ExDco6rNCCuXxSkzonn9cjBHWdNubmbdn/qkx65tkoDmMHBJ9oajeX+Zg09TQrODP1pTHceJIhRPnn/B+OiZauY9ci1tEqPGRwZXXdlV1oqcnwVoljRScEAAhsqxkNE6FLTKJFtI2Dk1N7w16m9oK7Gwm1Z+eXpWXnzmEsVr4XSAGZwTLh105d19jpnok1sUhjdV6UMrBZreJ3DCiiXmUGnd4yJhCmLE+DB4bHw/VGjNW/Brv2b9m/6ikVANvXSAGZwpHReyxGxhsqAqYpTeZSURIjK7kUaV1EC2d39oniQ3P6HG6F0+1nT9BcWF5z+lTkU3yRLA5jAMT3244wKa7kz0UZRQOymf3ktB4uoJDmHEJhqrPXnp7rD1CXO0oC97/1gTJs5g6k0oCmG4pnAMSmm8EAtn2jzrGTzAMVDwKQzVzPJRqgbxIOs5SFpaR0xqyGh+repvSFtzj1Qcd7EvTnn48Nvb2F2fZvMKj0mcIzpsqHBwTla6c0a2AFTqRoTn6OlUbxASGCGxCDrFBMJWf8YCHf36wTfHTVas2asK/zsBEMovomUBlCDY/ptRUkV5l/4RJuw/tUX1ggWIUoDsj79ouDFfwzgxeved09VvZyTv9EHPRK0pQHU4EiPLdxZY73Cr2ijqdlQpHmK1LyqeKVkAKUAGhYcCkJYAPTjGb3g6VecpQGb3z55buo8xtKAIF2lRw2OsV0KLts4a/tAsIYWoPQaZBwwxPfHmTx+ld7L8YCAcvaHKvvKRcVHVhQy65GgKg2gAocz0fYLv6ItWFmDWIiKYhs0JsVL9LpOvj3uJpj2ZgL06d8Jju3/xZwzt2A7kx4Jol0DqMAxJWZnfpXtUuZVYw3GASUSooRur9a1+j8aC8+9mQBIvH7096/5UPyPDPERVBpwtVfpUYEjrcsH5y0OczQra9C4j6RuMJP7SgAyEpOi1EbhvGdeiYcnpzlLA9YuPFL2Uj57KP5qlQYQg2NGt5KORvN/y3HAYBF0YpHJOsia3o6KoORHTihiJwj740wKrjShY2wkjH8lHoaN7Q7ffmG0vvP2gS9WbfVJj6BdjAK2awAxODK67smubDTmiDdh0YorsM4+xRkZoIioGzAk4XqS+AsA3NM/Csa/Gs/rkc+2nTXl5W7afYAlPhLgVXrE4JgQvflLk11Y0SZdqhisrOEvcNKYR3EbHkntzoMk8oYI2FPwg/GZWWyh+ECt0iMGR0rUOo6VNahcThEdURX0KJynJR6x7dIwRVTnyfQNcn1HPdcbxr96DxjPm7hVrx88uXg92yo9f2/gSwQOlGgzWoyuFW141pDYXh87mPpahBSvlcdhZRvF82QekBhUnWIjITMvAZB38+8vjNbcnF37Cz8LrtIAInBMitl2oMZa7dw6EhPVlIhL0WwJVtbwpxClAXb8gCiYkZcA3eNuhJKtZ02ZsxcXsLi+/lilRwSO0Z3fcSbaVGIBNB2i5gLiQKaHZ8PEGqxuL+VEGp7WnWcS9Lfp7ZPnpuUwhuJ13MBXExzTY4qSjNZfnSvaQqzhXsCNc19pJomcvSLbRcDEufEw6rleUH7exL3x4seH17C7vj6XBmiCIz16+84r9irX1pGePUS1Zr/EzZXrDxV9QN25hFpDKx6C0wxaoharNUgZR0FIR8VGwtz1AyF+QCf45nOjNStzXWHpycCXBmiCY2zn9y9bOUt7EtYg6khSBqKIa6gCkfB+WrEVnEYhEaK0rq9wzfiBUfDahgF8KH7XhlNVryzI3/gTYyieZQNfVXCgRFu5+VdXoo03LIJ1kewf6v7ex8BRsLEGbUSUuv0KrCEHG/qcMr0XTJrrLA1YvfBI2avL2UPxNBv4qoJjSszu/MuWi/wLcpQenLUzaM6j8nY03Gfc7BezBu0sp2FLXD+SfN+mXQuYufQBGJ7aHX76rsr+1uvFR9ay6xGiDXxVwZHa5cPzFjtKtJGzBs1gimcJDWBYz6PRScHCGp5+cf6v+903wQtLEwC5wJ8XnzfnZL+3nUmPuEsD8Kv0sOBAibbyhp/LaYAhmQEkmoHEDJFqBnFDRck0pvZTemWaM19JNBOYFDkwPKdwkJjcFWYuewCiYiLhg7dOGhfnr97KqkdwpQFYcEyJ2ZN92VKRo6c5EXcisQCUgYN19mMZjcIUMQGNENxquk0w6s77e1rRpl0EpEzrDWOm/wFMNRbug7ePn341P4d5lZ68NAALjmc6F35Z56hxbR3pahKh26hbsImwY/W2+6ztl4tJVhPrBooMDHJwoFFB7zjqHNsG0ufeCyNS7+Bd3/UrDnzBqkfEq/Sw4Hiy03rxsxF7J8y2moDKdWcNJWonaIeeINDUWiKmcO3l6RaA/Gf3CHJw74DOkD63L9w7IAr2b/3J9PzsNwtYTU2jI3yNIjgyYvdlXGy84E60ee2koxTgIZzlrGIS62lQmAVNbUD6XEr3JD2XQGtI2ynmC6lhc70ZzQUWz3GPpt0Bs/Ie4r/fWfC9ceKsTPpVehz3tSI4JkRvO1BrcybaaGYrEb1rDKaUrtgiskTR0CbHGl7Q5k2K888bQG3atYRZeQ/CiNQevGClBogDzIrgSOn0boMD7K1ogEEUQSWYNVp2WpOGfTALRODW4Rnkw4w1UwqD7oGD1KTIIeL8zMG+H8dCmxtawk0dhuS47RHhf7zAgRJt5ZZfS/QGBq4D/DkggXoGkolBysLKg6zNGgIYxOOOwDFmWhxvYjKf3rhv9ca15FtJKDHHJJRos1WNpOlY5gFWmuVK2oXQSyIeJAXTxvwMPmoNLdbADbpYiCoByskbAH0HdIa1nz5GDw4lzTEmypNo88Iro/ijAZquAxwAUBG3l8AcSfubXoiKYyCCZyMwR8/bn1n+n/+dvUJoUQBsYZslZmVGdFHcbzZnok0vYGBnBiaCKr7vVQOVEnsRDK6WXqLVGkqsIcQ2xCPkHf8QeANg2bah0L7D9db7Box6nRgYAGCxHZonAUf6rbvzq1QSbboBhtScKAyImP4lQJKzWoABrqopCIBFzBroQBUvRQwo5LEcNI6H1Qs/P5eZM4d8/3f+9SKHpcwxNupD19aRyllYyQMQeAWBmvkk1C5uiyqo/MgaRJNLEvTyOsPLfcVqEgBITrsT5q0bBEPuz9l46MQB4rd9c4awXejlzW4MokTbb/U/87vx+tNd9IpBaMyqQLVFdeYzai3Sa5KxhstUaLCGx6AA5GwYDHf1udne656/LKQzKWGL0co6960mR+/JvmxVTrTRMga2U3wwJ7prFwLmIx1cnNbAthnn4ejIGujehyomwP4dP1aNTp/6d1JwoL3LrI7DfETVDY5xKNFmFyXalGaLj27b1XIXr9p9MayIBw3OQ6FnjUGPdYOlhUNhzLAV+7aXbCWOb3CGMLTTEP9SRB4cgkkJFIWL70O7gJpahCoNEClr+DBB/McangeSeynixNyspQ/B4Mdu42JvGz6flDXQcRZb2HLhvTI8OKbcui/josU70cZkTkjtsx+FHyvIJaD1lSUZWQMrMDW1hlhtAB82//exclPymIlLScGB1uA2Og4vF47nb/lM520Hau2uRFsTHly/i2lCwFxt1ujR5yb46NiT8MqUPSfy1i8lflums5ajtFgCjic7uhJtPtAordtKMpC4mAaJx0PiuooFp4RtZI2jDW7h+kJLayixBm3QC11j8ty+8NS0u6mTbS1sYWvE748xzOl2rP+P9d8eUhRt/qB+f1wzEAEvQtbAgVIODLlyUPosBQZea0gMigFg8/En4OKFevPgEalLSE0KWmtrcRxaLD7e8GK3o7P+U/9dru6KnqEzWbWCFwuRilAKoJIE2sRMJG+TXqyB1SSuUe3cNRL2nk6FJVkHyrJp6kn5lxwe3uUFjp/qvssVvtSLQnFgw5kTHDAkxxPkY7CDEAAPJRhYg0+05T4Evbqmr6AqEbSFbbZAaZkUHLHHhv/U8O1e3CBofk9K6RSzVHcWIwWGEuPQtBt3PlHQy8tXogqV84Nq4GDZ1mHQrsP11vsZEm1yE8R7K6M7eXIqivSJeWjiQaTpYFJvSQmUMloSLqUobGWAwdM+2sbb0220zEpmTrwNI64+VJbc8MRUDQBoqcLB8gnMiTZFcEzuUpR0xV5ZIF/dpmZDcQLWl470xTsJhDlR7Q+CCSSFgAdx8qpyJQ9FS2sg1khORYm2wcyJNkVwCF9O6Lwnu9pWOcfB2fmNWqjcUxUB6j0vPNk93UQo6ezWYDFNM0rBgoFkDTRaOeuTfEq0qYID/TjxlpKOtutql1RbK8cpFv0QzBDV8DjN+QxaQTy4OHPiBVbK+A7L+f5mDXT9Q8aJPiXaNMEhHDAxuiiuzlJVWO+o7yF8hw3u6Jhtxc1csa0nGhwGYDGBGgP2QLPGoGSUaBvmU6KNGBweU1OUUm29uErYwEUCFBzFUlAvic6gNnGEYMUPIJkIJT1fiTWcQNTHQ0FXmpXXz+dEGzU4hBPGddydX22//CzHudazaACDaHb7GiiT3YTWkyAR1arADBLWQM3YdybV50QbMzjQiUiPWKDhwyq78WHsrNFwMZkEKGYQfPKMaIClwYRqE4GUNeTrXj0MLWUXiWdjcP52x90dYLMOiTafwOFhkaKkenvVygaXHmGy1TJG1WXW+9GceAEgiFhj8qt/1CXRpgs4hIuk3rwvo9ZeucAKlvaCCZUwA8GMl3Q6g4gUn++rd6J3/sQX1lDSJEqsgY7bcvxJXRJtuoJDuNjom3cWmOzVo+yu+IjcThPpDxVgSABAYw4ooqhqkWEv2Ujg+ioBw/OddAqxREMFMRsV2wb28Ym2krLs/AXkG7coJNr8Ag500bRbiuLMjrrlJrWiIRoBSsA6qqZIQSdgdZKsXXJwk7Ah/triSKi3ceJ/pazyQtFQ4UqobkOvRJvfwOE2NR2KUkyOqnlmBT3il5oRmoHFzHgWc6LGhnjWIHVdMSByA8kDjnwdE21+B4fb1Ny4J7uWq860oQ1ucQOoMLtxgTbx93rqFOyMV0rMYdjsarEGWtF2qHwCrFp45NzzOS9Tr2iTgyFg4EA3Gn1LSUervXZJra1ynPjGJJ6J2swMZnOixBr6BbykqiU5tQfkrEvSLdEWUHC4WaR9UVy9oXaD2V7Tl2RgWYChqhNozIkGm7GZEwUzIVmlLIaPTKyKR0ykNdDX89cLK9r+yrSi7aoyh/zmo9oXpZi4S7lWR6P3xrcYytYziqlpjnBt0NWcKC9Qct5CO+AllreHjZNg/44zVaPTM5hWtAUVOITGPNFuT3aN49IcBzjf4eIvnYHXAtLcCWkb1FxaJXOiNOCsRcPyfb8GJd8Gy3ROtF0Vs6KEUKRHGi1X1tQ4vF/XIR8s0hlPGo8gjWlgRbCawNZa74oexgfX1dmXHMzmE223+7SiLSiZQ9yo0e2L4mrtVYVmrr6HxNoq6QQKD4LKbdbNnHguRCdCFTSJ0Ek8kLw1ySdn0nRPtAUNc8gb8nj7vSk1tspVdrC295vO0KFaTW6q8OZEYcCJRKhMe8hEKPq1B59oGwWvTNl1Im99PvOKtqBnDnkDk9vuLKh3VI2S6BFXP/sWqWTTGfIhxusYtUgoqwhVZo1nX73PL4m2oGUOialpXdLRFFb9kYlTeSOlAmDwA+dkZi9w4a6BMTMkrKFtTtRMj8xfUWANdPaW46Pg4oU68+ARaT6taGtyzCFu8OM3FCXV2qtWNnKeUkV317pGGscmXqSO0TDe5K9c/EwCDPmwO5/FNeA6iFB0NbQJ/r7TaX5JtDUJ5pA38rG2+zLq7JcW2JAewcxqas/CB3CR6ww1c6KgSYQHx4hQ9LMz0dZPlxVtTZo5xI1Pbl3S0R5Wu4TXI5zDufW26I9KxAadOdEWocKjrv90JIRfZ7DeP2A09daRWmBokswhBUlRnAXqltcLegS71FBdZwSvOZEbJ880iGzXEg6XT/Rboq3Jg0N4gOTIvSn1XM28RvnSCeEAFQGqBgyv32QUJfdJlHWGS2vIdIbysWQiFJ37aOqdMN+PibZrBhzCgwz/3Z7sWrg0h5OZGuKSQRkaSM2VUmDK07nONyiJP+NAhCv9c54rheLyrY9AdLe29l73+CfRds2BAz0Q0iMWrnaJiXOWBpCuhXF3v4I4Vb0OQ4jcxSWS/pcCA29OhHP/ZUynT7Rx3M9W+2HyWg9RC1VfHSpHUrB/Tm5dFFfnuLLBzJmk76YTTUKWQJr3HBbPay+uwWyd4G3MJGdiYhoCMFCiLb/wEV1XtGmN5zUFDrepuX5viomryrVCY7SYnUljInLWYNMZ2kzgHhxVYDivs2D9w/DHgV242NtGMG8dqQWGa9Ks4B76T9fvzq9zVD7LgdT1VYuJBKM5QW0qOjMO/n3sgil5zCTirSPFuxHTAgMdf00yh7gjkB6ps11ZUw9VI4XvcYE0dmAwBrv4EZAH3MU85fw/SrRtOZbi90Rbs2IO8cMOjSiKazBUFTZCfQ+lrSV8AQZ5AY9ysEtugOSfZ+f1h8dS7/J560ha9rjmmUPeIUNa7suoN1xaYHe4QvGiA7A1IBLvRE2A0ugMPCQEESo0rfB4ClRcqDMnUSTa5LsR0wKjWZgVXKcMithZ0ABVo5AeYWcN5aSa0rB7VYgSiFB0nc6xbeGT02mwOOvTstfyF+q6ok0LMM2OOcQdktS6pKPNUv1RA7hKA1w/Bso70TIn6HeUaJud2z8gibZmqznUZsmQVkVJ9Y6qlVZJqaJ68Q67zqAwPQCw4dO/QEOd3ZL0aOobWjPd/bvCbsTE54oObNbMIe+wQdftyW4wVGXahV0D3EyioDMkRtnbw/Bcmzx3Ivdb0NaRh8vTYeXCI+dm+mFFmxZgQuCQ9RAyNVZLzRKzS494V3bR6AzyVLzSfZJT74L56x7224q2EDi0egDze1JEUVwjV7vcLNMjgTIn6D75W4dDdLcb7L0DlGgLaQ5KsCRG7E1pgMpcO4dC8eTZVjrvRNk9PlI+Gba/960x/YWZ/DvXSP44HxJtIXCQ9LDCMQMjdmTXcZfncAbnBr7ydLq8fJk8qSa9lnAdZ6JtOIwe9va+HSXbmN7Rxvio7tNCmoOiB5Na7+5osVqW1INn1wBvrUAeBXXCwisdyGd1F6x7GO4bGB3QRFuIOSjAgDs0KWJ3XD2YNjRCbV8pY8iB4WEF5dgJPtpafPpp+OKzc35bJE3SDSHmIOklzDH9I3anmMMurkKheMHU+GpOEGv0iLsZCo+lwMtTdp1Y6scVbVqPHgKHVg8R/P5Qyy35Zq76WQcIekQ926pmTtBvWbko0dYz4Im2kFkhGGyWQ5AeqbfVrTFz1SOdhRBiNSI1H1qv0Cg8Ngb+e+ay6bHR5LUbeiTaQuBgGXmKc/q32plk5qpX2kC8a4B6BJWHkovD0Yq2orKnYXHW/rLX8hcFNNEWAgfFQPtyaL+W2zIaDJcX2F0b5ilJUze7iJYwjH2uD8zOHQD9419OO/7NkS4QBi7XWaM1Cu9o86X96NyQ5vC1BzXOT2i1qaCRq+ZLA3BuqyBm0b8b9v8NHDZD9f2Du7QHSGwXEW4fCgbDnaq30SnRFmIOP4NB6fJIj9Taqz9q5GoGeX73jra2uaEl/Kt8Mmxbe/rgE5PvTBSObQWJXe1h9qGGMEMnxeZzXJnFfniz3o8WYg69e1Tleg9evzWlkbsyz+baNcCz8MmpSVAp4Py1Q+Dvc04Mm7H4Xvdrw4VLtmiR2Mdgdwz1MjV+MCkhsxJAYIhv9UCrzdlmuJxpB1t7cRBteeEIiPl9O+vve94UgW9aYqtW10GCw+FIQMeEhYUdNdtKS/3xKCHm8EevElzz/tabOho4+5IGh1OPOANfo+Hd3O/Xjs/qPZngEn4/JAQOv3ex+g36R37SE+yN2U9M63bvjZ1a7x87s3vGVW6S+/YhcATLSARhO/4P0yEMQmY6kykAAAAASUVORK5CYII=","e":1},{"id":"image_4","w":291,"h":284,"u":"","p":"data:image/png;base64,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","e":1}],"layers":[{"ddd":1,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":11,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":45,"ix":10},"or":{"a":0,"k":[292,0,353],"ix":7},"p":{"a":0,"k":[51.2175,139.747,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[21.5,18.7615,43],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":22,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":4,"ix":10},"g":{"p":3,"k":{"a":0,"k":[0,0.949,0.43,0.249,0.5,0.945,0.319,0.289,1,0.941,0.207,0.328],"ix":8}},"s":{"a":0,"k":[0,0],"ix":4},"e":{"a":0,"k":[100,0],"ix":5},"t":1,"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":13},"bm":0,"nm":"Gradient Stroke 1","mn":"ADBE Vector Graphic - G-Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":80,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":119.000004846969,"s":[720]}],"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120.0000048877,"st":0,"bm":0},{"ddd":1,"ind":2,"ty":2,"nm":"Layer 11","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[91.398,72.2725,0],"to":[0,-1.667,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[91.398,67.2725,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[91.398,72.2725,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":91,"s":[91.398,67.2725,0],"to":[0,0,0],"ti":[0,-1.667,0]},{"t":119.000004846969,"s":[91.398,72.2725,0]}],"ix":2},"a":{"a":0,"k":[32.334,57.477,0],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"ip":0,"op":120.0000048877,"st":0,"bm":0},{"ddd":1,"ind":3,"ty":2,"nm":"Layer 9","parent":5,"refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[99.575,54.653,0],"ix":2},"a":{"a":0,"k":[13.379,8.382,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":120.0000048877,"st":0,"bm":0},{"ddd":1,"ind":4,"ty":2,"nm":"Layer 8","parent":5,"refId":"image_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[52.384,56.472,0],"ix":2},"a":{"a":0,"k":[38.333,32.236,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":120.0000048877,"st":0,"bm":0},{"ddd":1,"ind":5,"ty":2,"nm":"Layer 7","parent":6,"refId":"image_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[197.62,101.429,0],"ix":2},"a":{"a":0,"k":[67.454,66.635,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":18,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":22,"s":[120,120,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":26,"s":[70,70,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":30,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":90,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":94,"s":[70,70,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":98,"s":[120,120,100]},{"t":102.000004154545,"s":[0,0,100]}],"ix":6}},"ao":0,"ip":0,"op":120.0000048877,"st":0,"bm":0},{"ddd":1,"ind":6,"ty":2,"nm":"Layer 6","refId":"image_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[152.6655,98.2715,0],"to":[0,1.667,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":31,"s":[152.6655,103.2715,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":61,"s":[152.6655,98.2715,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":93,"s":[152.6655,103.2715,0],"to":[0,0,0],"ti":[0,1.667,0]},{"t":119.000004846969,"s":[152.6655,98.2715,0]}],"ix":2},"a":{"a":0,"k":[145.174,141.543,0],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"ip":0,"op":120.0000048877,"st":0,"bm":0},{"ddd":1,"ind":7,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[150,112.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-61,-32],[-162,38],[9,136],[129,65]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":12,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":4,"ix":10},"g":{"p":3,"k":{"a":0,"k":[0,0.949,0.43,0.249,0.5,0.945,0.319,0.289,1,0.941,0.207,0.328],"ix":8}},"s":{"a":0,"k":[0,0],"ix":4},"e":{"a":0,"k":[100,0],"ix":5},"t":1,"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":13},"bm":0,"nm":"Gradient Stroke 1","mn":"ADBE Vector Graphic - G-Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":80,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":119.000004846969,"s":[-360]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120.0000048877,"st":0,"bm":0},{"ddd":1,"ind":8,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":11,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":45,"ix":10},"or":{"a":0,"k":[292,0,358],"ix":7},"p":{"a":0,"k":[81.173,91.3375,72.9015],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[34.0275,34.584,78],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":22,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":4,"ix":10},"g":{"p":3,"k":{"a":0,"k":[0,0.949,0.43,0.249,0.5,0.945,0.319,0.289,1,0.941,0.207,0.328],"ix":8}},"s":{"a":0,"k":[0,0],"ix":4},"e":{"a":0,"k":[100,0],"ix":5},"t":1,"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":13},"bm":0,"nm":"Gradient Stroke 1","mn":"ADBE Vector Graphic - G-Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":80,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":119.000004846969,"s":[-720]}],"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":120.0000048877,"st":0,"bm":0}],"markers":[]}
|
|
|
|
animations/loginUser.json
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.20","a":"","k":"","d":"","tc":""},"fr":25,"ip":0,"op":50,"w":1000,"h":1000,"nm":"User","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Layer 1 Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[100]},{"t":15,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500,500.305,0],"ix":2},"a":{"a":0,"k":[428.342,494.37,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,93.473],[-93.473,0],[0,-93.473],[93.473,0]],"o":[[0,-93.473],[93.473,0],[0,93.473],[-93.473,0]],"v":[[-169.248,0],[0,-169.248],[169.248,0],[0,169.248]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0.4627,0.498,0.6784,1]},{"t":5,"s":[0.1554,0.2269,0.5846,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":47,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[428.342,286.748],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.001,14.488],[0,0],[3.082,17.225],[4.699,14.677],[7.919,13.93],[10.273,9.831],[14.821,5.866],[16.294,0.436],[0,0],[0,0],[4.363,-2.841],[12.785,-8.173],[19.383,-6.263],[19.525,0],[19.406,6.275],[12.139,7.738],[10.029,6.535],[3.059,1.627],[0,0],[0,0],[13.646,-5.404],[10.29,-9.852],[7.921,-13.929],[4.695,-14.664],[3.08,-17.146],[1.023,-14.798],[0,-15.601],[-21.466,-20.433],[-14.643,-5.103],[-17.417,0],[0,0],[-13.942,4.859],[-10.993,10.463],[-5.376,14.366],[-0.002,17.229]],"o":[[0,0],[-1.018,-14.698],[-3.067,-17.108],[-4.69,-14.654],[-7.922,-13.941],[-10.304,-9.856],[-13.649,-5.403],[0,0],[0,0],[-3.01,1.602],[-9.316,6.075],[-12.104,7.718],[-19.405,6.271],[-19.525,0],[-19.363,-6.249],[-11.744,-7.501],[-4.325,-2.821],[0,0],[0,0],[-16.292,0.436],[-14.835,5.869],[-10.271,9.837],[-7.912,13.93],[-4.682,14.626],[-3.078,17.128],[-1.023,14.691],[0,35.256],[10.992,10.461],[13.941,4.858],[0,0],[17.415,0],[14.644,-5.103],[10.988,-10.454],[5.101,-13.633],[-0.003,-15.378]],"v":[[309.329,24.263],[309.285,23.609],[303.106,-24.499],[291.402,-72.396],[272.399,-115.473],[244.978,-151.299],[207.114,-174.991],[161.988,-183.791],[155.657,-183.96],[150.067,-180.982],[138.954,-174.286],[106.117,-153.117],[58.651,-132.039],[-0.017,-122.589],[-58.687,-132.045],[-106.156,-153.121],[-138.979,-174.28],[-150.113,-180.988],[-155.7,-183.96],[-162.025,-183.791],[-207.142,-174.989],[-245.011,-151.294],[-272.426,-115.479],[-291.424,-72.391],[-303.121,-24.511],[-309.301,23.603],[-310.842,69.26],[-278.492,153.185],[-239.861,176.639],[-192.603,183.96],[192.598,183.96],[239.853,176.637],[278.488,153.178],[303.151,115.771],[310.842,69.262]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0.4627,0.498,0.6784,1]},{"t":5,"s":[0.1554,0.2269,0.5846,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":47,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[428.342,687.279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"t":15,"s":[0]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"t":15,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"t":15,"s":[-100]}],"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":250,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Layer 1 Outlines 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[500.001,500,0],"ix":2},"a":{"a":0,"k":[428.342,494.37,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":15,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[105,105,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":25,"s":[97,97,100]},{"t":30,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":15,"s":[{"i":[[0,93.473],[-93.473,0],[0,-93.473],[93.473,0]],"o":[[0,-93.473],[93.473,0],[0,93.473],[-93.473,0]],"v":[[-169.248,0],[0,-169.248],[169.248,0],[0,169.248]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,93.473],[-93.473,0],[0,-93.473],[93.473,0]],"o":[[0,-93.473],[93.473,0],[0,93.473],[-93.473,0]],"v":[[-168.75,-23.81],[0.498,-193.058],[169.746,-23.81],[0.498,145.439]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":25,"s":[{"i":[[0,93.473],[-93.473,0],[0,-93.473],[93.473,0]],"o":[[0,-93.473],[93.473,0],[0,93.473],[-93.473,0]],"v":[[-168.723,41.237],[0.525,-128.011],[169.773,41.237],[0.525,210.485]],"c":true}]},{"t":30,"s":[{"i":[[0,93.473],[-93.473,0],[0,-93.473],[93.473,0]],"o":[[0,-93.473],[93.473,0],[0,93.473],[-93.473,0]],"v":[[-169.248,0],[0,-169.248],[169.248,0],[0,169.248]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0.4627,0.498,0.6784,1]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[0.4627,0.498,0.6784,1]},{"t":15,"s":[0.1554,0.2269,0.5846,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":47,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[428.342,286.748],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.001,14.488],[0,0],[3.082,17.225],[4.699,14.677],[7.919,13.93],[10.273,9.831],[14.821,5.866],[16.294,0.436],[0,0],[0,0],[4.363,-2.841],[12.785,-8.173],[19.383,-6.263],[19.525,0],[19.406,6.275],[12.139,7.738],[10.029,6.535],[3.059,1.627],[0,0],[0,0],[13.646,-5.404],[10.29,-9.852],[7.921,-13.929],[4.695,-14.664],[3.08,-17.146],[1.023,-14.798],[0,-15.601],[-21.466,-20.433],[-14.643,-5.103],[-17.417,0],[0,0],[-13.942,4.859],[-10.993,10.463],[-5.376,14.366],[-0.002,17.229]],"o":[[0,0],[-1.018,-14.698],[-3.067,-17.108],[-4.69,-14.654],[-7.922,-13.941],[-10.304,-9.856],[-13.649,-5.403],[0,0],[0,0],[-3.01,1.602],[-9.316,6.075],[-12.104,7.718],[-19.405,6.271],[-19.525,0],[-19.363,-6.249],[-11.744,-7.501],[-4.325,-2.821],[0,0],[0,0],[-16.292,0.436],[-14.835,5.869],[-10.271,9.837],[-7.912,13.93],[-4.682,14.626],[-3.078,17.128],[-1.023,14.691],[0,35.256],[10.992,10.461],[13.941,4.858],[0,0],[17.415,0],[14.644,-5.103],[10.988,-10.454],[5.101,-13.633],[-0.003,-15.378]],"v":[[309.329,24.263],[309.285,23.609],[303.106,-24.499],[291.402,-72.396],[272.399,-115.473],[244.978,-151.299],[207.114,-174.991],[161.988,-183.791],[155.657,-183.96],[150.067,-180.982],[138.954,-174.286],[106.117,-153.117],[58.651,-132.039],[-0.017,-122.589],[-58.687,-132.045],[-106.156,-153.121],[-138.979,-174.28],[-150.113,-180.988],[-155.7,-183.96],[-162.025,-183.791],[-207.142,-174.989],[-245.011,-151.294],[-272.426,-115.479],[-291.424,-72.391],[-303.121,-24.511],[-309.301,23.603],[-310.842,69.26],[-278.492,153.185],[-239.861,176.639],[-192.603,183.96],[192.598,183.96],[239.853,176.637],[278.488,153.178],[303.151,115.771],[310.842,69.262]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0.4627,0.498,0.6784,1]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[0.4627,0.498,0.6784,1]},{"t":15,"s":[0.1554,0.2269,0.5846,1]}],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":47,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[428.342,687.279],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":250,"st":0,"bm":0}],"markers":[]}
|
|
|
|
app.py
DELETED
@@ -1,32 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
from paginas import login, dashboardDemo
|
3 |
-
|
4 |
-
|
5 |
-
def main():
|
6 |
-
# Configurar la página y el estado de la sesión (solo una vez en app.py)
|
7 |
-
st.set_page_config(
|
8 |
-
page_title=":beginner: Dashboard Sales", # Título de la página
|
9 |
-
page_icon=":smile:", # Icono de la página
|
10 |
-
layout="wide", # Configurar el layout para que ocupe todo el ancho
|
11 |
-
initial_sidebar_state="expanded" # Barra lateral expandida por defecto
|
12 |
-
)
|
13 |
-
|
14 |
-
# Leer parámetros de la URL
|
15 |
-
query_params = st.query_params
|
16 |
-
logged_in = query_params.get("logged_in", ["False"])[0] == "True"
|
17 |
-
|
18 |
-
# Verificar si el usuario está logueado
|
19 |
-
if logged_in or ("logged_in" in st.session_state and st.session_state.get("logged_in", False)):
|
20 |
-
st.session_state.logged_in = True # Asegurar consistencia interna del estado
|
21 |
-
if "archivo_subido" not in st.session_state:
|
22 |
-
st.session_state.archivo_subido = False
|
23 |
-
dashboardDemo.mostrar_dashboard()
|
24 |
-
else:
|
25 |
-
# Si no, mostramos el login
|
26 |
-
login.showLogin()
|
27 |
-
# Si inicia sesión correctamente, actualiza el parámetro en la URL
|
28 |
-
if "logged_in" in st.session_state and st.session_state.logged_in:
|
29 |
-
st.query_params.set(logged_in="True")
|
30 |
-
|
31 |
-
if __name__ == "__main__":
|
32 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
catboost.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:059f80be82f0f5ace74dc7ee09cd8a24502bb2e106623f43404a65c1df9abd0f
|
3 |
-
size 1115713
|
|
|
|
|
|
|
|
dbpredictivesystemgenai.sql
DELETED
@@ -1,74 +0,0 @@
|
|
1 |
-
-- phpMyAdmin SQL Dump
|
2 |
-
-- version 5.2.1
|
3 |
-
-- https://www.phpmyadmin.net/
|
4 |
-
--
|
5 |
-
-- Servidor: 127.0.0.1
|
6 |
-
-- Tiempo de generación: 19-06-2025 a las 19:52:38
|
7 |
-
-- Versión del servidor: 10.4.32-MariaDB
|
8 |
-
-- Versión de PHP: 8.2.12
|
9 |
-
|
10 |
-
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
11 |
-
START TRANSACTION;
|
12 |
-
SET time_zone = "+00:00";
|
13 |
-
|
14 |
-
|
15 |
-
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
16 |
-
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
17 |
-
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
18 |
-
/*!40101 SET NAMES utf8mb4 */;
|
19 |
-
|
20 |
-
--
|
21 |
-
-- Base de datos: `dbpredictivesystemgenai`
|
22 |
-
--
|
23 |
-
|
24 |
-
-- --------------------------------------------------------
|
25 |
-
|
26 |
-
--
|
27 |
-
-- Estructura de tabla para la tabla `usuarios`
|
28 |
-
--
|
29 |
-
|
30 |
-
CREATE TABLE `usuarios` (
|
31 |
-
`id` int(11) NOT NULL,
|
32 |
-
`nombre` varchar(20) NOT NULL,
|
33 |
-
`apellido` varchar(30) NOT NULL,
|
34 |
-
`correo` varchar(30) NOT NULL,
|
35 |
-
`telefono` varchar(9) NOT NULL,
|
36 |
-
`username` varchar(30) NOT NULL,
|
37 |
-
`password` varchar(50) NOT NULL
|
38 |
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
39 |
-
|
40 |
-
--
|
41 |
-
-- Volcado de datos para la tabla `usuarios`
|
42 |
-
--
|
43 |
-
|
44 |
-
INSERT INTO `usuarios` (`id`, `nombre`, `apellido`, `correo`, `telefono`, `username`, `password`) VALUES
|
45 |
-
(1, 'Administrador', 'Admin', '[email protected]', '985615263', 'admin', 'pass_admin'),
|
46 |
-
(2, 'Juan Pablo', 'Perez Gonzalez', '[email protected]', '978451223', 'juanperez', 'pass_juanperez'),
|
47 |
-
(3, 'Carlos Eduardo', 'Luna Martinez', '[email protected]', '978456129', 'carlosluna', 'pass_carlosluna'),
|
48 |
-
(4, 'Maria Gabriela', 'Gomez Rodriguez', '[email protected]', '998565236', 'mariagomez', 'pass_mariagomez'),
|
49 |
-
(5, 'Ana Lucia', 'Hernandez Diaz', '[email protected]', '963365125', 'analucia', 'pass_analucia');
|
50 |
-
|
51 |
-
--
|
52 |
-
-- Índices para tablas volcadas
|
53 |
-
--
|
54 |
-
|
55 |
-
--
|
56 |
-
-- Indices de la tabla `usuarios`
|
57 |
-
--
|
58 |
-
ALTER TABLE `usuarios`
|
59 |
-
ADD PRIMARY KEY (`id`);
|
60 |
-
|
61 |
-
--
|
62 |
-
-- AUTO_INCREMENT de las tablas volcadas
|
63 |
-
--
|
64 |
-
|
65 |
-
--
|
66 |
-
-- AUTO_INCREMENT de la tabla `usuarios`
|
67 |
-
--
|
68 |
-
ALTER TABLE `usuarios`
|
69 |
-
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=31;
|
70 |
-
COMMIT;
|
71 |
-
|
72 |
-
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
73 |
-
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
74 |
-
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
df_articles.csv
DELETED
The diff for this file is too large to render.
See raw diff
|
|
gradientboosting.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:5bb28622f5a612db81f8c5d12f56913710303da5778551e6931dd97393ebad1c
|
3 |
-
size 145876
|
|
|
|
|
|
|
|
histgradientboosting.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:13dca14204a9cbdd7bb407ae68697b9d4acc5b4969119b5abd2301f8c6e674cd
|
3 |
-
size 375945
|
|
|
|
|
|
|
|
lightgbm.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:ae50d6dfc075ff1b930fcb207169710ab452f384a006ed4a3c61e3d2baae35f5
|
3 |
-
size 281459
|
|
|
|
|
|
|
|
mlpregressor.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:287880615fc3e4cfedbba56d537bb7217ea86808ed896d81fd75b43f3b596387
|
3 |
-
size 261329
|
|
|
|
|
|
|
|
paginas/__init__.py
DELETED
File without changes
|
paginas/conexionMysql.py
DELETED
@@ -1,24 +0,0 @@
|
|
1 |
-
from contextlib import contextmanager
|
2 |
-
import MySQLdb
|
3 |
-
import os
|
4 |
-
from dotenv import load_dotenv
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
load_dotenv()
|
9 |
-
|
10 |
-
|
11 |
-
@contextmanager
|
12 |
-
def get_db_connection():
|
13 |
-
connection = MySQLdb.connect(
|
14 |
-
host=os.environ["DB_HOST"],
|
15 |
-
port=int(os.environ["DB_PORT"]),
|
16 |
-
user=os.environ["DB_USER"],
|
17 |
-
passwd=os.environ["DB_PASSWORD"],
|
18 |
-
db=os.environ["DB_NAME"]
|
19 |
-
)
|
20 |
-
|
21 |
-
try:
|
22 |
-
yield connection
|
23 |
-
finally:
|
24 |
-
connection.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
paginas/conexionTest.py
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
|
|
|
|
paginas/dashboard.py
DELETED
@@ -1,813 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import pandas as pd
|
3 |
-
import plotly.express as px
|
4 |
-
import random
|
5 |
-
import time
|
6 |
-
import joblib
|
7 |
-
import os
|
8 |
-
import statsmodels
|
9 |
-
from dotenv import load_dotenv
|
10 |
-
import os
|
11 |
-
from groq import Groq
|
12 |
-
import html
|
13 |
-
from pydub import AudioSegment
|
14 |
-
import tempfile
|
15 |
-
from io import BytesIO
|
16 |
-
import tempfile
|
17 |
-
#from langchain.agents.agent_toolkits import create_csv_agent
|
18 |
-
#from langchain_groq import ChatGroq
|
19 |
-
# ===========================
|
20 |
-
# Función para generar datos ficticios
|
21 |
-
# ===========================
|
22 |
-
def generar_datos():
|
23 |
-
meses = [
|
24 |
-
"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio",
|
25 |
-
"Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"
|
26 |
-
]
|
27 |
-
paises = ["México", "Colombia", "Argentina", "Chile", "Perú"]
|
28 |
-
data = [
|
29 |
-
{"mes": mes, "pais": pais, "Total": random.randint(100, 1000)}
|
30 |
-
for mes in meses for pais in paises
|
31 |
-
]
|
32 |
-
return pd.DataFrame(data), meses, paises
|
33 |
-
|
34 |
-
# ===========================
|
35 |
-
# Función para el dashboard principal
|
36 |
-
# ===========================
|
37 |
-
def mostrar_dashboard():
|
38 |
-
# Cargar variables desde el archivo .env
|
39 |
-
load_dotenv()
|
40 |
-
|
41 |
-
# Acceder a la clave
|
42 |
-
groq_key = os.getenv("GROQ_API_KEY")
|
43 |
-
client = Groq(api_key=groq_key)
|
44 |
-
|
45 |
-
dfDatos, meses, paises = generar_datos()
|
46 |
-
|
47 |
-
# Opciones del selectbox
|
48 |
-
lista_opciones = ['5 años', '3 años', '1 año', '5 meses']
|
49 |
-
|
50 |
-
# Mostrar barra lateral
|
51 |
-
mostrar_sidebar(client)
|
52 |
-
|
53 |
-
# Título principal
|
54 |
-
st.header(':bar_chart: Dashboard Sales')
|
55 |
-
|
56 |
-
# Mostrar métricas
|
57 |
-
#mostrar_metricas()
|
58 |
-
|
59 |
-
# Mostrar gráficos
|
60 |
-
mostrar_graficos(lista_opciones)
|
61 |
-
|
62 |
-
# ===========================
|
63 |
-
# Configuración inicial de la página
|
64 |
-
# ===========================
|
65 |
-
#def configurar_pagina():
|
66 |
-
#st.set_page_config(
|
67 |
-
# page_title="Dashboard Sales",
|
68 |
-
# page_icon=":smile:",
|
69 |
-
# layout="wide",
|
70 |
-
# initial_sidebar_state="expanded"
|
71 |
-
#)
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
# ===========================
|
76 |
-
# Función para la barra lateral
|
77 |
-
# ===========================
|
78 |
-
def mostrar_sidebar(client):
|
79 |
-
sidebar_logo = r"paginas\images\Logo general.png"
|
80 |
-
main_body_logo = r"paginas\images\Logo.png"
|
81 |
-
sidebar_logo_dashboard = r"paginas\images\Logo dashboard.png"
|
82 |
-
|
83 |
-
st.logo(sidebar_logo, size="large", icon_image=main_body_logo)
|
84 |
-
|
85 |
-
st.sidebar.image(sidebar_logo_dashboard)
|
86 |
-
st.sidebar.title('🧠 GenAI Forecast')
|
87 |
-
|
88 |
-
loadCSV()
|
89 |
-
|
90 |
-
archivo_csv = "df_articles.csv"
|
91 |
-
chatBotProtech(client)
|
92 |
-
downloadCSV(archivo_csv)
|
93 |
-
|
94 |
-
|
95 |
-
# Mostrar la tabla solo si se ha subido un archivo válido
|
96 |
-
'''
|
97 |
-
if 'archivo_subido' in st.session_state and st.session_state.archivo_subido: # Verificamos si el archivo ha sido subido y es válido
|
98 |
-
st.sidebar.markdown("Vista previa del archivo CSV:")
|
99 |
-
# Usar st.dataframe() para que ocupe todo el ancho disponible
|
100 |
-
st.sidebar.dataframe(st.session_state.df_subido, use_container_width=True) # Mostrar la tabla con el archivo subido
|
101 |
-
'''
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
if st.sidebar.button("Cerrar Sesión"):
|
106 |
-
cerrar_sesion()
|
107 |
-
|
108 |
-
|
109 |
-
# ===========================
|
110 |
-
# Función para métricas principales
|
111 |
-
# ===========================
|
112 |
-
'''
|
113 |
-
def mostrar_metricas():
|
114 |
-
c1, c2, c3, c4, c5 = st.columns(5)
|
115 |
-
valores = [89, 78, 67, 56, 45]
|
116 |
-
for i, col in enumerate([c1, c2, c3, c4, c5]):
|
117 |
-
valor1 = valores[i]
|
118 |
-
valor2 = valor1 - 10 # Simulación de variación
|
119 |
-
variacion = valor1 - valor2
|
120 |
-
unidad = "unidades" if i < 4 else "%"
|
121 |
-
col.metric(f"Productos vendidos", f'{valor1:,.0f} {unidad}', f'{variacion:,.0f}')
|
122 |
-
'''
|
123 |
-
|
124 |
-
|
125 |
-
# Función para obtener los meses relevantes
|
126 |
-
def obtener_meses_relevantes(df):
|
127 |
-
# Extraemos los años y meses de la columna 'Date'
|
128 |
-
df['Year'] = pd.to_datetime(df['orddt']).dt.year
|
129 |
-
df['Month'] = pd.to_datetime(df['orddt']).dt.month
|
130 |
-
|
131 |
-
# Encontramos el primer y último año en el dataset
|
132 |
-
primer_ano = df['Year'].min()
|
133 |
-
ultimo_ano = df['Year'].max()
|
134 |
-
|
135 |
-
meses_relevantes = []
|
136 |
-
nombres_meses_relevantes = []
|
137 |
-
|
138 |
-
# Recorrer todos los años dentro del rango
|
139 |
-
for ano in range(primer_ano, ultimo_ano + 1):
|
140 |
-
for mes in [1, 4, 7, 10]: # Meses relevantes: enero (1), abril (4), julio (7), octubre (10)
|
141 |
-
if mes in df[df['Year'] == ano]['Month'].values:
|
142 |
-
# Obtener el nombre del mes
|
143 |
-
nombre_mes = pd.to_datetime(f"{ano}-{mes}-01").strftime('%B') # Mes en formato textual (Enero, Abril, etc.)
|
144 |
-
meses_relevantes.append(f"{nombre_mes}-{ano}")
|
145 |
-
nombres_meses_relevantes.append(f"{nombre_mes}-{ano}")
|
146 |
-
|
147 |
-
return meses_relevantes, nombres_meses_relevantes
|
148 |
-
|
149 |
-
# ===========================
|
150 |
-
# Función para gráficos
|
151 |
-
# ===========================
|
152 |
-
def mostrar_graficos(lista_opciones):
|
153 |
-
|
154 |
-
"""
|
155 |
-
c1, c2 = st.columns([20, 80])
|
156 |
-
|
157 |
-
with c1:
|
158 |
-
filtroAnios = st.selectbox('Año', options=lista_opciones)
|
159 |
-
|
160 |
-
with c2:
|
161 |
-
st.markdown("### :pushpin: Ventas actuales")
|
162 |
-
# Si hay un archivo válido subido
|
163 |
-
if "archivo_subido" in st.session_state and st.session_state.archivo_subido:
|
164 |
-
# Cargar datos del archivo subido
|
165 |
-
df = st.session_state.df_subido.copy()
|
166 |
-
df['Date'] = pd.to_datetime(df['Date'])
|
167 |
-
df['Mes-Año'] = df['Date'].dt.strftime('%B-%Y') # Formato deseado
|
168 |
-
df = df.sort_values('Date') # Ordenar por fecha
|
169 |
-
|
170 |
-
# Obtener los meses relevantes del dataset
|
171 |
-
meses_relevantes, nombres_meses_relevantes = obtener_meses_relevantes(df)
|
172 |
-
|
173 |
-
# Crear la gráfica
|
174 |
-
fig = px.line(
|
175 |
-
df,
|
176 |
-
x='Mes-Año',
|
177 |
-
y='Sale',
|
178 |
-
title='Ventas mensuales (Archivo Subido)',
|
179 |
-
labels={'Mes-Año': 'Mes-Año', 'Sale': 'Ventas'},
|
180 |
-
)
|
181 |
-
else:
|
182 |
-
# Datos por defecto
|
183 |
-
df = pd.DataFrame({
|
184 |
-
"Mes-Año": ["Enero-2024", "Febrero-2024", "Marzo-2024", "Abril-2024", "Mayo-2024", "Junio-2024", "Julio-2024", "Agosto-2024", "Septiembre-2024", "Octubre-2024", "Noviembre-2024", "Diciembre-2024"],
|
185 |
-
"Sale": [100, 150, 120, 200, 250, 220, 280, 300, 350, 400, 450, 500],
|
186 |
-
})
|
187 |
-
|
188 |
-
# Obtener los meses relevantes
|
189 |
-
meses_relevantes = ["Enero-2024", "Abril-2024", "Julio-2024", "Octubre-2024"]
|
190 |
-
nombres_meses_relevantes = ["Enero-2024", "Abril-2024", "Julio-2024", "Octubre-2024"]
|
191 |
-
|
192 |
-
# Crear la gráfica
|
193 |
-
fig = px.line(
|
194 |
-
df,
|
195 |
-
x='Mes-Año',
|
196 |
-
y='Sale',
|
197 |
-
title='Ventas mensuales (Datos por defecto)',
|
198 |
-
labels={'Mes-Año': 'Mes-Año', 'Sale': 'Ventas'},
|
199 |
-
line_shape='linear' # Línea continua
|
200 |
-
)
|
201 |
-
|
202 |
-
|
203 |
-
fig.update_xaxes(tickangle=-45) # Ajustar ángulo de etiquetas en X
|
204 |
-
|
205 |
-
# Mejorar el diseño de la gráfica
|
206 |
-
fig = mejorar_diseno_grafica(fig, meses_relevantes, nombres_meses_relevantes)
|
207 |
-
st.plotly_chart(fig, use_container_width=True) # Evita que ocupe todo el ancho
|
208 |
-
|
209 |
-
# Gráfica 2: Ventas actuales y proyectadas
|
210 |
-
st.markdown("### :chart_with_upwards_trend: Pronóstico")
|
211 |
-
mostrar_ventas_proyectadas(filtroAnios)
|
212 |
-
"""
|
213 |
-
if "archivo_subido" not in st.session_state or not st.session_state.archivo_subido:
|
214 |
-
st.warning("Por favor, sube un archivo CSV válido para visualizar los gráficos.")
|
215 |
-
return
|
216 |
-
|
217 |
-
df = st.session_state.df_subido.copy()
|
218 |
-
|
219 |
-
# Fila 1: 3 gráficas
|
220 |
-
col1, col2, col3 = st.columns(3)
|
221 |
-
with col1:
|
222 |
-
fig1 = px.histogram(df, x='sales', title='Distribución de Ventas')
|
223 |
-
st.plotly_chart(fig1, use_container_width=True)
|
224 |
-
|
225 |
-
with col2:
|
226 |
-
fig2 = px.box(df, x='segmt', y='sales', title='Ventas por Segmento')
|
227 |
-
st.plotly_chart(fig2, use_container_width=True)
|
228 |
-
|
229 |
-
with col3:
|
230 |
-
print("")
|
231 |
-
|
232 |
-
# Fila 2: 2 gráficas
|
233 |
-
col4, col5 = st.columns(2)
|
234 |
-
with col4:
|
235 |
-
fig4 = px.pie(df, names='categ', values='sales', title='Ventas por Categoría')
|
236 |
-
st.plotly_chart(fig4, use_container_width=True)
|
237 |
-
|
238 |
-
with col5:
|
239 |
-
|
240 |
-
# Agrupar por nombre de producto y sumar las ventas
|
241 |
-
top_productos = (
|
242 |
-
df.groupby('prdna')['sales']
|
243 |
-
.sum()
|
244 |
-
.sort_values(ascending=False)
|
245 |
-
.head(10)
|
246 |
-
.reset_index()
|
247 |
-
)
|
248 |
-
|
249 |
-
# Crear gráfica de barras horizontales
|
250 |
-
fig5 = px.bar(
|
251 |
-
top_productos,
|
252 |
-
x='sales',
|
253 |
-
y='prdna',
|
254 |
-
orientation='h',
|
255 |
-
title='Top 10 productos más vendidos',
|
256 |
-
labels={'sales': 'Ventas', 'prdna': 'Producto'},
|
257 |
-
color='sales',
|
258 |
-
color_continuous_scale='Blues'
|
259 |
-
)
|
260 |
-
|
261 |
-
fig5.update_layout(yaxis={'categoryorder': 'total ascending'})
|
262 |
-
st.plotly_chart(fig5, use_container_width=True)
|
263 |
-
|
264 |
-
col6, col7 = st.columns(2)
|
265 |
-
with col6:
|
266 |
-
# Fuera del sistema de columnas
|
267 |
-
tabla = df.pivot_table(index='state', columns='subct', values='sales', aggfunc='sum').fillna(0)
|
268 |
-
|
269 |
-
if not tabla.empty:
|
270 |
-
tabla = tabla.astype(float)
|
271 |
-
fig6 = px.imshow(
|
272 |
-
tabla.values,
|
273 |
-
labels=dict(x="Categoría", y="Estado", color="Ventas"),
|
274 |
-
x=tabla.columns,
|
275 |
-
y=tabla.index,
|
276 |
-
text_auto=True,
|
277 |
-
title="Mapa de Calor: Ventas por Estado y Categoría"
|
278 |
-
)
|
279 |
-
|
280 |
-
# Ajuste del tamaño de la figura
|
281 |
-
# fig6.update_layout(height=600, width=1000) # Puedes ajustar según tu pantalla
|
282 |
-
st.plotly_chart(fig6, use_container_width=True)
|
283 |
-
else:
|
284 |
-
st.warning("No hay datos suficientes para mostrar el mapa de calor.")
|
285 |
-
|
286 |
-
|
287 |
-
with col7:
|
288 |
-
fig7 = px.bar(df.groupby('state')['sales'].sum().reset_index(), x='state', y='sales', title='Ventas por Estado')
|
289 |
-
st.plotly_chart(fig7, use_container_width=True)
|
290 |
-
|
291 |
-
# -------------------------------
|
292 |
-
# CARGA DE CSV Y GUARDADO EN SESIÓN
|
293 |
-
# -------------------------------
|
294 |
-
|
295 |
-
def loadCSV():
|
296 |
-
columnas_requeridas = [
|
297 |
-
'rowid','ordid','orddt',
|
298 |
-
'shpdt','segmt','state',
|
299 |
-
'cono','prodid','categ',
|
300 |
-
'subct','prdna','sales'
|
301 |
-
]
|
302 |
-
with st.sidebar.expander("📁 Subir archivo"):
|
303 |
-
uploaded_file = st.file_uploader("Sube un archivo CSV:", type=["csv"], key="upload_csv")
|
304 |
-
|
305 |
-
|
306 |
-
if uploaded_file is not None:
|
307 |
-
# Reseteamos el estado de 'descargado' cuando se sube un archivo
|
308 |
-
st.session_state.descargado = False
|
309 |
-
st.session_state.archivo_subido = False # Reinicia el estado
|
310 |
-
try:
|
311 |
-
# Leer el archivo subido
|
312 |
-
df = pd.read_csv(uploaded_file)
|
313 |
-
|
314 |
-
# Verificar que las columnas estén presentes y en el orden correcto
|
315 |
-
if list(df.columns) == columnas_requeridas:
|
316 |
-
st.session_state.df_subido = df
|
317 |
-
st.session_state.archivo_subido = True
|
318 |
-
aviso = st.sidebar.success("✅ Archivo subido correctamente.")
|
319 |
-
time.sleep(3)
|
320 |
-
aviso.empty()
|
321 |
-
|
322 |
-
|
323 |
-
else:
|
324 |
-
st.session_state.archivo_subido = False
|
325 |
-
aviso = st.sidebar.error(f"El archivo no tiene las columnas requeridas: {columnas_requeridas}.")
|
326 |
-
time.sleep(3)
|
327 |
-
aviso.empty()
|
328 |
-
|
329 |
-
except Exception as e:
|
330 |
-
aviso = st.sidebar.error(f"Error al procesar el archivo: {str(e)}")
|
331 |
-
time.sleep(3)
|
332 |
-
aviso.empty()
|
333 |
-
|
334 |
-
# ===========================
|
335 |
-
# Función para descargar archivo CSV
|
336 |
-
# ===========================
|
337 |
-
def downloadCSV(archivo_csv):
|
338 |
-
# Verificamos si el archivo ya ha sido descargado
|
339 |
-
if 'descargado' not in st.session_state:
|
340 |
-
st.session_state.descargado = False
|
341 |
-
|
342 |
-
if not st.session_state.descargado:
|
343 |
-
|
344 |
-
# Usamos st.spinner para mostrar un estado de descarga inicial
|
345 |
-
#with st.spinner("Preparando archivo para descarga..."):
|
346 |
-
# time.sleep(2) # Simulación de preparación del archivo
|
347 |
-
# Botón de descarga
|
348 |
-
descarga = st.sidebar.download_button(
|
349 |
-
label="Descargar archivo CSV",
|
350 |
-
data=open(archivo_csv, "rb"),
|
351 |
-
file_name="ventas.csv",
|
352 |
-
mime="text/csv"
|
353 |
-
)
|
354 |
-
|
355 |
-
if descarga:
|
356 |
-
# Marcamos el archivo como descargado
|
357 |
-
st.session_state.descargado = True
|
358 |
-
aviso = st.sidebar.success("¡Descarga completada!")
|
359 |
-
# Hacer que el mensaje desaparezca después de 2 segundos
|
360 |
-
time.sleep(3)
|
361 |
-
aviso.empty()
|
362 |
-
else:
|
363 |
-
aviso = st.sidebar.success("¡Ya has descargado el archivo!")
|
364 |
-
time.sleep(3)
|
365 |
-
aviso.empty()
|
366 |
-
|
367 |
-
# -------------------------------
|
368 |
-
# CREACIÓN DE AGENTE CSV
|
369 |
-
# -------------------------------
|
370 |
-
'''
|
371 |
-
def createCSVAgent(client, df):
|
372 |
-
temp_csv = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
|
373 |
-
df.to_csv(temp_csv.name, index=False)
|
374 |
-
agent = create_csv_agent(
|
375 |
-
client,
|
376 |
-
temp_csv.name,
|
377 |
-
verbose=False,
|
378 |
-
handle_parsing_errors=True
|
379 |
-
)
|
380 |
-
return agent
|
381 |
-
'''
|
382 |
-
'''
|
383 |
-
def callCSVAgent(client, prompt):
|
384 |
-
if "df_csv" not in st.session_state:
|
385 |
-
return "No hay CSV cargado aún."
|
386 |
-
|
387 |
-
df = st.session_state.df_csv
|
388 |
-
agente = createCSVAgent(client, df)
|
389 |
-
|
390 |
-
try:
|
391 |
-
respuesta = agente.run(prompt)
|
392 |
-
except Exception as e:
|
393 |
-
respuesta = f"Error al procesar la pregunta: {e}"
|
394 |
-
|
395 |
-
return respuesta
|
396 |
-
'''
|
397 |
-
|
398 |
-
# -------------------------------
|
399 |
-
# FUNCIÓN PARA DETECTAR REFERENCIA AL CSV
|
400 |
-
# -------------------------------
|
401 |
-
def detectedReferenceToCSV(prompt: str) -> bool:
|
402 |
-
palabras_clave = ["csv", "archivo", "contenido cargado", "file", "dataset"]
|
403 |
-
prompt_lower = prompt.lower()
|
404 |
-
return any(palabra in prompt_lower for palabra in palabras_clave)
|
405 |
-
|
406 |
-
# ===========================
|
407 |
-
# Función para interactuar con el bot
|
408 |
-
# ===========================
|
409 |
-
def chatBotProtech(client):
|
410 |
-
with st.sidebar.expander("📁 Chatbot"):
|
411 |
-
|
412 |
-
# Inicializar estados
|
413 |
-
if "chat_history" not in st.session_state:
|
414 |
-
st.session_state.chat_history = []
|
415 |
-
|
416 |
-
if "audio_data" not in st.session_state:
|
417 |
-
st.session_state.audio_data = None
|
418 |
-
|
419 |
-
if "transcripcion" not in st.session_state:
|
420 |
-
st.session_state.transcripcion = ""
|
421 |
-
|
422 |
-
if "mostrar_grabador" not in st.session_state:
|
423 |
-
st.session_state.mostrar_grabador = True
|
424 |
-
|
425 |
-
# Contenedor para mensajes
|
426 |
-
messages = st.container(height=400)
|
427 |
-
|
428 |
-
|
429 |
-
# CSS: estilo tipo Messenger
|
430 |
-
st.markdown("""
|
431 |
-
<style>
|
432 |
-
.chat-message {
|
433 |
-
display: flex;
|
434 |
-
align-items: flex-start;
|
435 |
-
margin: 10px 0;
|
436 |
-
}
|
437 |
-
.chat-message.user {
|
438 |
-
justify-content: flex-end;
|
439 |
-
}
|
440 |
-
.chat-message.assistant {
|
441 |
-
justify-content: flex-start;
|
442 |
-
}
|
443 |
-
.chat-icon {
|
444 |
-
width: 30px;
|
445 |
-
height: 30px;
|
446 |
-
border-radius: 50%;
|
447 |
-
background-color: #ccc;
|
448 |
-
display: flex;
|
449 |
-
align-items: center;
|
450 |
-
justify-content: center;
|
451 |
-
font-size: 18px;
|
452 |
-
margin: 0 5px;
|
453 |
-
}
|
454 |
-
.chat-bubble {
|
455 |
-
max-width: 70%;
|
456 |
-
padding: 10px 15px;
|
457 |
-
border-radius: 15px;
|
458 |
-
font-size: 14px;
|
459 |
-
line-height: 1.5;
|
460 |
-
word-wrap: break-word;
|
461 |
-
}
|
462 |
-
.chat-bubble.user {
|
463 |
-
background-color: #DCF8C6;
|
464 |
-
color: black;
|
465 |
-
border-top-right-radius: 0;
|
466 |
-
}
|
467 |
-
.chat-bubble.assistant {
|
468 |
-
background-color: #F1F0F0;
|
469 |
-
color: black;
|
470 |
-
border-top-left-radius: 0;
|
471 |
-
}
|
472 |
-
</style>
|
473 |
-
""", unsafe_allow_html=True)
|
474 |
-
|
475 |
-
# Mostrar historial de mensajes
|
476 |
-
with messages:
|
477 |
-
st.header("🤖 ChatBot Protech")
|
478 |
-
for message in st.session_state.chat_history:
|
479 |
-
role = message["role"]
|
480 |
-
content = html.escape(message["content"]) # Escapar contenido HTML
|
481 |
-
bubble_class = "user" if role == "user" else "assistant"
|
482 |
-
icon = "👤" if role == "user" else "🤖"
|
483 |
-
|
484 |
-
# Mostrar el mensaje en una sola burbuja con ícono en el mismo bloque
|
485 |
-
st.markdown(f"""
|
486 |
-
<div class="chat-message {bubble_class}">
|
487 |
-
<div class="chat-icon">{icon}</div>
|
488 |
-
<div class="chat-bubble {bubble_class}">{content}</div>
|
489 |
-
</div>
|
490 |
-
""", unsafe_allow_html=True)
|
491 |
-
|
492 |
-
# --- Manejar transcripción como mensaje automático ---
|
493 |
-
if st.session_state.transcripcion:
|
494 |
-
prompt = st.session_state.transcripcion
|
495 |
-
st.session_state.transcripcion = ""
|
496 |
-
|
497 |
-
st.session_state.chat_history.append({"role": "user", "content": prompt})
|
498 |
-
|
499 |
-
with messages:
|
500 |
-
st.markdown(f"""
|
501 |
-
<div class="chat-message user">
|
502 |
-
<div class="chat-bubble user">{html.escape(prompt)}</div>
|
503 |
-
<div class="chat-icon">👤</div>
|
504 |
-
</div>
|
505 |
-
""", unsafe_allow_html=True)
|
506 |
-
|
507 |
-
with messages:
|
508 |
-
with st.spinner("Pensando..."):
|
509 |
-
completion = callDeepseek(client, prompt)
|
510 |
-
response = ""
|
511 |
-
response_placeholder = st.empty()
|
512 |
-
|
513 |
-
for chunk in completion:
|
514 |
-
content = chunk.choices[0].delta.content or ""
|
515 |
-
response += content
|
516 |
-
response_placeholder.markdown(f"""
|
517 |
-
<div class="chat-message assistant">
|
518 |
-
<div class="chat-icon">🤖</div>
|
519 |
-
<div class="chat-bubble assistant">{response}</div>
|
520 |
-
</div>
|
521 |
-
""", unsafe_allow_html=True)
|
522 |
-
|
523 |
-
st.session_state.chat_history.append({"role": "assistant", "content": response})
|
524 |
-
|
525 |
-
# Captura del input tipo chat
|
526 |
-
if prompt := st.chat_input("Escribe algo..."):
|
527 |
-
st.session_state.chat_history.append({"role": "user", "content": prompt})
|
528 |
-
|
529 |
-
# Mostrar mensaje del usuario escapado
|
530 |
-
with messages:
|
531 |
-
|
532 |
-
st.markdown(f"""
|
533 |
-
<div class="chat-message user">
|
534 |
-
<div class="chat-bubble user">{prompt}</div>
|
535 |
-
<div class="chat-icon">👤</div>
|
536 |
-
</div>
|
537 |
-
""", unsafe_allow_html=True)
|
538 |
-
|
539 |
-
# Mostrar respuesta del asistente
|
540 |
-
with messages:
|
541 |
-
with st.spinner("Pensando..."):
|
542 |
-
completion = callDeepseek(client, prompt)
|
543 |
-
response = ""
|
544 |
-
response_placeholder = st.empty()
|
545 |
-
|
546 |
-
for chunk in completion:
|
547 |
-
content = chunk.choices[0].delta.content or ""
|
548 |
-
response += content
|
549 |
-
|
550 |
-
response_placeholder.markdown(f"""
|
551 |
-
<div class="chat-message assistant">
|
552 |
-
<div class="chat-icon">🤖</div>
|
553 |
-
<div class="chat-bubble assistant">{response}</div>
|
554 |
-
</div>
|
555 |
-
""", unsafe_allow_html=True)
|
556 |
-
|
557 |
-
st.session_state.chat_history.append({"role": "assistant", "content": response})
|
558 |
-
|
559 |
-
# Grabación de audio (solo si está habilitada)
|
560 |
-
if st.session_state.mostrar_grabador and st.session_state.audio_data is None:
|
561 |
-
audio_data = st.audio_input("Graba tu voz aquí 🎤")
|
562 |
-
if audio_data:
|
563 |
-
st.session_state.audio_data = audio_data
|
564 |
-
st.session_state.mostrar_grabador = False # Ocultar input después de grabar
|
565 |
-
st.rerun() # Forzar recarga para ocultar input y evitar que reaparezca el audio cargado
|
566 |
-
|
567 |
-
# Mostrar controles solo si hay audio cargado
|
568 |
-
if st.session_state.audio_data:
|
569 |
-
st.audio(st.session_state.audio_data, format="audio/wav")
|
570 |
-
col1, col2 = st.columns(2)
|
571 |
-
|
572 |
-
with col1:
|
573 |
-
if st.button("✅ Aceptar grabación"):
|
574 |
-
with st.spinner("Convirtiendo y transcribiendo..."):
|
575 |
-
m4a_path = converter_bytes_m4a(st.session_state.audio_data)
|
576 |
-
|
577 |
-
with open(m4a_path, "rb") as f:
|
578 |
-
texto = callWhisper(client, m4a_path, f)
|
579 |
-
|
580 |
-
os.remove(m4a_path)
|
581 |
-
|
582 |
-
st.session_state.transcripcion = texto
|
583 |
-
st.session_state.audio_data = None
|
584 |
-
st.session_state.mostrar_grabador = True
|
585 |
-
st.rerun()
|
586 |
-
|
587 |
-
with col2:
|
588 |
-
if st.button("❌ Descartar grabación"):
|
589 |
-
st.session_state.audio_data = None
|
590 |
-
st.session_state.transcripcion = ""
|
591 |
-
st.session_state.mostrar_grabador = True
|
592 |
-
st.rerun()
|
593 |
-
|
594 |
-
# Mostrar transcripción como texto previo al input si existe
|
595 |
-
'''
|
596 |
-
if st.session_state.transcripcion:
|
597 |
-
st.info(f"📝 Transcripción: {st.session_state.transcripcion}")
|
598 |
-
# Prellenar el input simuladamente
|
599 |
-
prompt = st.session_state.transcripcion
|
600 |
-
st.session_state.transcripcion = "" # Limpiar
|
601 |
-
st.rerun() # Simular que se envió el mensaje
|
602 |
-
'''
|
603 |
-
|
604 |
-
#def speechRecognition():
|
605 |
-
#audio_value = st.audio_input("Record a voice message")
|
606 |
-
|
607 |
-
def callDeepseek(client, prompt):
|
608 |
-
completion = client.chat.completions.create(
|
609 |
-
#model="meta-llama/llama-4-scout-17b-16e-instruct",
|
610 |
-
model = "deepseek-r1-distill-llama-70b",
|
611 |
-
messages=[{"role": "user", "content": prompt}],
|
612 |
-
temperature=0.6,
|
613 |
-
max_tokens=1024,
|
614 |
-
top_p=1,
|
615 |
-
stream=True,
|
616 |
-
)
|
617 |
-
return completion
|
618 |
-
|
619 |
-
def callWhisper(client, filename_audio,file):
|
620 |
-
transcription = client.audio.transcriptions.create(
|
621 |
-
file=(filename_audio, file.read()),
|
622 |
-
model="whisper-large-v3",
|
623 |
-
response_format="verbose_json",
|
624 |
-
)
|
625 |
-
return transcription.text
|
626 |
-
|
627 |
-
def converter_bytes_m4a(audio_bytes: BytesIO) -> str:
|
628 |
-
"""
|
629 |
-
Convierte un audio en bytes (WAV, etc.) a un archivo M4A temporal.
|
630 |
-
Retorna la ruta del archivo .m4a temporal.
|
631 |
-
"""
|
632 |
-
# Asegurarse de que el cursor del stream esté al inicio
|
633 |
-
audio_bytes.seek(0)
|
634 |
-
|
635 |
-
# Leer el audio desde BytesIO usando pydub
|
636 |
-
audio = AudioSegment.from_file(audio_bytes)
|
637 |
-
|
638 |
-
# Crear archivo temporal para guardar como .m4a
|
639 |
-
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".m4a")
|
640 |
-
m4a_path = temp_file.name
|
641 |
-
temp_file.close() # Cerramos para que pydub pueda escribirlo
|
642 |
-
|
643 |
-
# Exportar a M4A usando formato compatible con ffmpeg
|
644 |
-
audio.export(m4a_path, format="ipod") # 'ipod' genera .m4a
|
645 |
-
|
646 |
-
return m4a_path
|
647 |
-
# ===========================
|
648 |
-
# Función para cargar el modelo SARIMA
|
649 |
-
# ===========================
|
650 |
-
"""def cargar_modelo_sarima(ruta_modelo):
|
651 |
-
# Cargar el modelo utilizando joblib
|
652 |
-
modelo = joblib.load(ruta_modelo)
|
653 |
-
return modelo"""
|
654 |
-
|
655 |
-
# ===========================
|
656 |
-
# Función para obtener el número de periodos basado en el filtro
|
657 |
-
# ===========================
|
658 |
-
def obtener_periodos(filtro):
|
659 |
-
opciones_periodos = {
|
660 |
-
'5 años': 60,
|
661 |
-
'3 años': 36,
|
662 |
-
'1 año': 12,
|
663 |
-
'5 meses': 5
|
664 |
-
}
|
665 |
-
return opciones_periodos.get(filtro, 12)
|
666 |
-
|
667 |
-
# ===========================
|
668 |
-
# Función para mostrar ventas actuales y proyectadas
|
669 |
-
# ===========================
|
670 |
-
"""
|
671 |
-
def mostrar_ventas_proyectadas(filtro):
|
672 |
-
ruta_modelo = os.path.join("arima_sales_model.pkl")
|
673 |
-
modelo_sarima = cargar_modelo_sarima(ruta_modelo)
|
674 |
-
|
675 |
-
if "archivo_subido" in st.session_state and st.session_state.archivo_subido:
|
676 |
-
# Cargar datos del archivo subido
|
677 |
-
df = st.session_state.df_subido.copy()
|
678 |
-
df['Date'] = pd.to_datetime(df['Date'])
|
679 |
-
df = df.sort_values('Date')
|
680 |
-
|
681 |
-
# Generar predicciones
|
682 |
-
periodos = obtener_periodos(filtro)
|
683 |
-
predicciones = generar_predicciones(modelo_sarima, df, periodos)
|
684 |
-
|
685 |
-
# Redondear y formatear las ventas
|
686 |
-
df['Sale'] = df['Sale'].round(2).apply(lambda x: f"{x:,.2f}") # Formato con 2 decimales y comas
|
687 |
-
predicciones = [round(val, 2) for val in predicciones] # Redondear predicciones
|
688 |
-
|
689 |
-
# Preparar datos para graficar
|
690 |
-
df['Tipo'] = 'Ventas Actuales'
|
691 |
-
df_pred = pd.DataFrame({
|
692 |
-
'Date': pd.date_range(df['Date'].max(), periods=periodos + 1, freq='ME')[1:],
|
693 |
-
'Sale': predicciones,
|
694 |
-
'Tipo': 'Ventas Pronosticadas'
|
695 |
-
})
|
696 |
-
|
697 |
-
df_grafico = pd.concat([df[['Date', 'Sale', 'Tipo']], df_pred])
|
698 |
-
else:
|
699 |
-
st.warning("Por favor, sube un archivo CSV válido para generasr predicciones.")
|
700 |
-
return
|
701 |
-
|
702 |
-
# Crear gráfica
|
703 |
-
fig = px.line(
|
704 |
-
df_grafico,
|
705 |
-
x='Date',
|
706 |
-
y='Sale',
|
707 |
-
color='Tipo',
|
708 |
-
title='Ventas pronosticadas (Ventas vs Mes)',
|
709 |
-
labels={'Date': 'Fecha', 'Sale': 'Ventas', 'Tipo': 'Serie'}
|
710 |
-
)
|
711 |
-
|
712 |
-
# Centramos el título del gráfico
|
713 |
-
fig.update_layout(
|
714 |
-
title={
|
715 |
-
'text': "Ventas Actuales y Pronosticadas",
|
716 |
-
|
717 |
-
'x': 0.5, # Centrado horizontal
|
718 |
-
'xanchor': 'center', # Asegura el anclaje central
|
719 |
-
'yanchor': 'top' # Anclaje superior (opcional)
|
720 |
-
},
|
721 |
-
title_font=dict(size=18, family="Arial, sans-serif", color='black'),
|
722 |
-
)
|
723 |
-
|
724 |
-
fig.update_xaxes(tickangle=-45)
|
725 |
-
|
726 |
-
# Mejorar el diseño de la leyenda
|
727 |
-
fig.update_layout(
|
728 |
-
legend=dict(
|
729 |
-
title="Leyenda", # Título de la leyenda
|
730 |
-
title_font=dict(size=12, color="black"),
|
731 |
-
font=dict(size=10, color="black"),
|
732 |
-
bgcolor="rgba(240,240,240,0.8)", # Fondo semitransparente
|
733 |
-
bordercolor="gray",
|
734 |
-
borderwidth=1,
|
735 |
-
orientation="h", # Leyenda horizontal
|
736 |
-
yanchor="top",
|
737 |
-
y=-0.3, # Ajustar la posición vertical
|
738 |
-
xanchor="right",
|
739 |
-
x=0.5 # Centrar horizontalmente
|
740 |
-
)
|
741 |
-
)
|
742 |
-
|
743 |
-
st.plotly_chart(fig, use_container_width=True)
|
744 |
-
"""
|
745 |
-
# ===========================
|
746 |
-
# Función para generar predicciones
|
747 |
-
# ===========================
|
748 |
-
def generar_predicciones(modelo, df, periodos):
|
749 |
-
ventas = df['Sale']
|
750 |
-
predicciones = modelo.forecast(steps=periodos)
|
751 |
-
return predicciones
|
752 |
-
|
753 |
-
# Función para mejorar el diseño de las gráficas
|
754 |
-
def mejorar_diseno_grafica(fig, meses_relevantes, nombres_meses_relevantes):
|
755 |
-
fig.update_layout(
|
756 |
-
title={
|
757 |
-
'text': "Ventas vs Mes",
|
758 |
-
|
759 |
-
'x': 0.5, # Centrado horizontal
|
760 |
-
'xanchor': 'center', # Asegura el anclaje central
|
761 |
-
'yanchor': 'top' # Anclaje superior (opcional)
|
762 |
-
},
|
763 |
-
title_font=dict(size=18, family="Arial, sans-serif", color='black'),
|
764 |
-
xaxis=dict(
|
765 |
-
title='Mes-Año',
|
766 |
-
title_font=dict(size=14, family="Arial, sans-serif", color='black'),
|
767 |
-
tickangle=-45, # Rotar las etiquetas
|
768 |
-
showgrid=True,
|
769 |
-
gridwidth=0.5,
|
770 |
-
gridcolor='lightgrey',
|
771 |
-
showline=True,
|
772 |
-
linecolor='black',
|
773 |
-
linewidth=2,
|
774 |
-
tickmode='array', # Controla qué etiquetas mostrar
|
775 |
-
tickvals=meses_relevantes, # Selecciona solo los meses relevantes
|
776 |
-
ticktext=nombres_meses_relevantes, # Meses seleccionados
|
777 |
-
tickfont=dict(size=10), # Reducir el tamaño de la fuente de las etiquetas
|
778 |
-
),
|
779 |
-
yaxis=dict(
|
780 |
-
title='Ventas',
|
781 |
-
title_font=dict(size=14, family="Arial, sans-serif", color='black'),
|
782 |
-
showgrid=True,
|
783 |
-
gridwidth=0.5,
|
784 |
-
gridcolor='lightgrey',
|
785 |
-
showline=True,
|
786 |
-
linecolor='black',
|
787 |
-
linewidth=2
|
788 |
-
),
|
789 |
-
plot_bgcolor='white', # Fondo blanco
|
790 |
-
paper_bgcolor='white', # Fondo del lienzo de la gráfica
|
791 |
-
font=dict(family="Arial, sans-serif", size=12, color="black"),
|
792 |
-
showlegend=False, # Desactivar la leyenda si no es necesaria
|
793 |
-
margin=dict(l=50, r=50, t=50, b=50) # Márgenes ajustados
|
794 |
-
)
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
return fig
|
799 |
-
|
800 |
-
# ===========================
|
801 |
-
# Función para cerrar sesión
|
802 |
-
# ===========================
|
803 |
-
def cerrar_sesion():
|
804 |
-
st.session_state.logged_in = False
|
805 |
-
st.session_state.usuario = None
|
806 |
-
st.session_state.pagina_actual = "login"
|
807 |
-
st.session_state.archivo_subido = False # Limpiar el archivo subido al cerrar sesión
|
808 |
-
st.session_state.df_subido = None # Limpiar datos del archivo
|
809 |
-
# Eliminar parámetros de la URL usando st.query_params
|
810 |
-
st.query_params.clear() # Método correcto para limpiar parámetros de consulta
|
811 |
-
|
812 |
-
# Redirigir a la página de login
|
813 |
-
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
paginas/dashboardDemo.py
DELETED
@@ -1,947 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import pandas as pd
|
3 |
-
import plotly.express as px
|
4 |
-
import random
|
5 |
-
import time
|
6 |
-
import joblib
|
7 |
-
import os
|
8 |
-
import statsmodels
|
9 |
-
from dotenv import load_dotenv
|
10 |
-
import os
|
11 |
-
from groq import Groq
|
12 |
-
import html
|
13 |
-
from pydub import AudioSegment
|
14 |
-
import tempfile
|
15 |
-
from io import BytesIO
|
16 |
-
from fpdf import FPDF
|
17 |
-
from PIL import Image
|
18 |
-
from math import ceil
|
19 |
-
from datetime import datetime
|
20 |
-
from sklearn.metrics import r2_score
|
21 |
-
#from langchain.agents.agent_toolkits import create_csv_agent
|
22 |
-
#from langchain_groq import ChatGroq
|
23 |
-
# ===========================
|
24 |
-
# Función para generar datos ficticios
|
25 |
-
# ===========================
|
26 |
-
def generar_datos():
|
27 |
-
meses = [
|
28 |
-
"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio",
|
29 |
-
"Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"
|
30 |
-
]
|
31 |
-
paises = ["México", "Colombia", "Argentina", "Chile", "Perú"]
|
32 |
-
data = [
|
33 |
-
{"mes": mes, "pais": pais, "Total": random.randint(100, 1000)}
|
34 |
-
for mes in meses for pais in paises
|
35 |
-
]
|
36 |
-
return pd.DataFrame(data), meses, paises
|
37 |
-
|
38 |
-
|
39 |
-
# ===========================
|
40 |
-
# Función para el dashboard principal
|
41 |
-
# ===========================
|
42 |
-
def mostrar_dashboard():
|
43 |
-
# Cargar variables desde el archivo .env
|
44 |
-
load_dotenv()
|
45 |
-
|
46 |
-
# Acceder a la clave
|
47 |
-
groq_key = os.getenv("GROQ_API_KEY")
|
48 |
-
client = Groq(api_key=groq_key)
|
49 |
-
|
50 |
-
dfDatos, meses, paises = generar_datos()
|
51 |
-
|
52 |
-
# Opciones del selectbox
|
53 |
-
lista_opciones = ['5 años', '3 años', '1 año', '5 meses']
|
54 |
-
|
55 |
-
# Mostrar barra lateral
|
56 |
-
mostrar_sidebar(client)
|
57 |
-
|
58 |
-
# Título principal
|
59 |
-
st.header(':bar_chart: Dashboard Sales')
|
60 |
-
|
61 |
-
# Mostrar gráficos
|
62 |
-
mostrar_graficos(lista_opciones)
|
63 |
-
|
64 |
-
# ===========================
|
65 |
-
# Configuración inicial de la página
|
66 |
-
# ===========================
|
67 |
-
#def configurar_pagina():
|
68 |
-
#st.set_page_config(
|
69 |
-
# page_title="Dashboard Sales",
|
70 |
-
# page_icon=":smile:",
|
71 |
-
# layout="wide",
|
72 |
-
# initial_sidebar_state="expanded"
|
73 |
-
#)
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
# ===========================
|
78 |
-
# Función para la barra lateral
|
79 |
-
# ===========================
|
80 |
-
def mostrar_sidebar(client):
|
81 |
-
"""
|
82 |
-
Windows
|
83 |
-
sidebar_logo = "paginas/images/Logo general.png"
|
84 |
-
main_body_logo = "paginas/images/Logo.png"
|
85 |
-
sidebar_logo_dashboard = "paginas/images/Logo dashboard.png"
|
86 |
-
|
87 |
-
"""
|
88 |
-
sidebar_logo = "paginas/images/Logo general.png"
|
89 |
-
main_body_logo = "paginas/images/Logo.png"
|
90 |
-
sidebar_logo_dashboard = "paginas/images/Logo dashboard.png"
|
91 |
-
|
92 |
-
|
93 |
-
st.logo(sidebar_logo, size="large", icon_image=main_body_logo)
|
94 |
-
|
95 |
-
st.sidebar.image(sidebar_logo_dashboard)
|
96 |
-
st.sidebar.title('🧠 GenAI Forecast')
|
97 |
-
|
98 |
-
uploaded_file = selectedFile()
|
99 |
-
verifyFile(uploaded_file)
|
100 |
-
archivo_csv = "df_articles.csv"
|
101 |
-
chatBotProtech(client)
|
102 |
-
downloadCSV(archivo_csv)
|
103 |
-
closeSession()
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
def closeSession():
|
108 |
-
if st.sidebar.button("Cerrar Sesión"):
|
109 |
-
cerrar_sesion()
|
110 |
-
|
111 |
-
|
112 |
-
def guardar_graficas_como_imagen(figuras: dict):
|
113 |
-
rutas_imagenes = []
|
114 |
-
temp_dir = tempfile.gettempdir()
|
115 |
-
|
116 |
-
for nombre, figura in figuras.items():
|
117 |
-
ruta_png = os.path.join(temp_dir, f"{nombre}.png")
|
118 |
-
ruta_jpeg = os.path.join(temp_dir, f"{nombre}.jpg")
|
119 |
-
|
120 |
-
# Guardar como PNG primero
|
121 |
-
figura.write_image(ruta_png, width=900, height=500, engine="kaleido")
|
122 |
-
|
123 |
-
# Convertir a JPEG usando PIL
|
124 |
-
with Image.open(ruta_png) as img:
|
125 |
-
rgb_img = img.convert("RGB") # Asegura formato compatible con JPEG
|
126 |
-
rgb_img.save(ruta_jpeg, "JPEG", quality=95)
|
127 |
-
|
128 |
-
rutas_imagenes.append((nombre, ruta_jpeg))
|
129 |
-
|
130 |
-
# Opcional: borrar el PNG temporal
|
131 |
-
os.remove(ruta_png)
|
132 |
-
|
133 |
-
return rutas_imagenes
|
134 |
-
|
135 |
-
def generateHeaderPDF(pdf):
|
136 |
-
# Logo
|
137 |
-
logo_path = r"paginas\images\Logo general.png"
|
138 |
-
if os.path.exists(logo_path):
|
139 |
-
pdf.image(logo_path, x=7, y=6, w=35)
|
140 |
-
|
141 |
-
# Título centrado
|
142 |
-
pdf.set_font('Arial', 'B', 16)
|
143 |
-
pdf.set_xy(5, 10)
|
144 |
-
pdf.cell(w=0, h=10, txt="Reporte del Dashboard de Ventas", border=0, ln=0, align='C')
|
145 |
-
|
146 |
-
# Fecha lado derecho
|
147 |
-
fecha = datetime.now().strftime("%d/%m/%Y")
|
148 |
-
pdf.set_xy(-40, 5)
|
149 |
-
pdf.set_font('Arial', '', 10)
|
150 |
-
pdf.cell(w=30, h=10, txt=fecha, border=0, ln=0, align='R')
|
151 |
-
|
152 |
-
pdf.ln(15)
|
153 |
-
|
154 |
-
def generateFooterPDF(pdf):
|
155 |
-
pdf.set_y(-30)
|
156 |
-
pdf.set_font('Arial', 'I', 8)
|
157 |
-
pdf.set_text_color(100)
|
158 |
-
pdf.cell(0, 5, "PRO TECHNOLOGY SOLUTIONS S.A.C - Área de ventas", 0, 1, 'C')
|
159 |
-
pdf.cell(0, 5, "Reporte generado automáticamente por el sistema de análisis", 0, 1, 'C')
|
160 |
-
pdf.cell(0, 5, f"Página {pdf.page_no()}", 0, 0, 'C')
|
161 |
-
|
162 |
-
def generateContentPDF(pdf, imagenes):
|
163 |
-
for i in range(0, len(imagenes), 2):
|
164 |
-
pdf.add_page()
|
165 |
-
|
166 |
-
generateHeaderPDF(pdf)
|
167 |
-
|
168 |
-
# Primera imagen
|
169 |
-
titulo1, ruta1 = imagenes[i]
|
170 |
-
if os.path.exists(ruta1):
|
171 |
-
img1 = Image.open(ruta1).convert("RGB")
|
172 |
-
ruta_color1 = ruta1.replace(".png", "_color.png")
|
173 |
-
img1.save(ruta_color1)
|
174 |
-
pdf.image(ruta_color1, x=10, y=30, w=180)
|
175 |
-
|
176 |
-
# Segunda imagen
|
177 |
-
if i + 1 < len(imagenes):
|
178 |
-
titulo2, ruta2 = imagenes[i + 1]
|
179 |
-
if os.path.exists(ruta2):
|
180 |
-
img2 = Image.open(ruta2).convert("RGB")
|
181 |
-
ruta_color2 = ruta2.replace(".png", "_color.png")
|
182 |
-
img2.save(ruta_color2)
|
183 |
-
pdf.image(ruta_color2, x=10, y=150, w=180)
|
184 |
-
|
185 |
-
generateFooterPDF(pdf)
|
186 |
-
|
187 |
-
def generar_reporte_dashboard(imagenes):
|
188 |
-
pdf = FPDF(orientation='P', unit='mm', format='A4')
|
189 |
-
pdf.set_auto_page_break(auto=True, margin=15)
|
190 |
-
|
191 |
-
generateContentPDF(pdf, imagenes)
|
192 |
-
|
193 |
-
ruta_pdf = "reporte.pdf"
|
194 |
-
pdf.output(ruta_pdf)
|
195 |
-
return ruta_pdf
|
196 |
-
|
197 |
-
|
198 |
-
# Función para obtener los meses relevantes
|
199 |
-
def obtener_meses_relevantes(df):
|
200 |
-
# Extraemos los años y meses de la columna 'Date'
|
201 |
-
df['Year'] = pd.to_datetime(df['orddt']).dt.year
|
202 |
-
df['Month'] = pd.to_datetime(df['orddt']).dt.month
|
203 |
-
|
204 |
-
# Encontramos el primer y último año en el dataset
|
205 |
-
primer_ano = df['Year'].min()
|
206 |
-
ultimo_ano = df['Year'].max()
|
207 |
-
|
208 |
-
meses_relevantes = []
|
209 |
-
nombres_meses_relevantes = []
|
210 |
-
|
211 |
-
# Recorrer todos los años dentro del rango
|
212 |
-
for ano in range(primer_ano, ultimo_ano + 1):
|
213 |
-
for mes in [1, 4, 7, 10]: # Meses relevantes: enero (1), abril (4), julio (7), octubre (10)
|
214 |
-
if mes in df[df['Year'] == ano]['Month'].values:
|
215 |
-
# Obtener el nombre del mes
|
216 |
-
nombre_mes = pd.to_datetime(f"{ano}-{mes}-01").strftime('%B') # Mes en formato textual (Enero, Abril, etc.)
|
217 |
-
meses_relevantes.append(f"{nombre_mes}-{ano}")
|
218 |
-
nombres_meses_relevantes.append(f"{nombre_mes}-{ano}")
|
219 |
-
|
220 |
-
return meses_relevantes, nombres_meses_relevantes
|
221 |
-
|
222 |
-
|
223 |
-
# ===========================
|
224 |
-
# Función para gráficos
|
225 |
-
# ===========================
|
226 |
-
def mostrar_graficos(lista_opciones):
|
227 |
-
if "archivo_subido" not in st.session_state or not st.session_state.archivo_subido:
|
228 |
-
st.warning("Por favor, sube un archivo CSV válido para visualizar los gráficos.")
|
229 |
-
return
|
230 |
-
|
231 |
-
df = st.session_state.df_subido.copy()
|
232 |
-
|
233 |
-
# --- Tarjetas con métricas clave ---
|
234 |
-
# Tasa de crecimiento por fecha si existe
|
235 |
-
total_ventas = df["sales"].sum()
|
236 |
-
promedio_ventas = df["sales"].mean()
|
237 |
-
|
238 |
-
st.subheader("📈 Resumen General")
|
239 |
-
|
240 |
-
|
241 |
-
# Tasa de crecimiento por fecha si existe
|
242 |
-
df['orddt'] = pd.to_datetime(df['orddt'], errors='coerce')
|
243 |
-
|
244 |
-
#Total de ventas
|
245 |
-
total_ventas = df['sales'].sum()
|
246 |
-
promedio_ventas = df['sales'].mean()
|
247 |
-
total_registros = df.shape[0]
|
248 |
-
|
249 |
-
# Tasa de crecimiento
|
250 |
-
df_filtrado = df.dropna(subset=['orddt'])
|
251 |
-
df_filtrado['mes_anio'] = df_filtrado['orddt'].dt.to_period('M')
|
252 |
-
ventas_por_mes = df_filtrado.groupby('mes_anio')['sales'].sum().sort_index()
|
253 |
-
|
254 |
-
tasa_crecimiento = None
|
255 |
-
if len(ventas_por_mes) >= 2:
|
256 |
-
primera_venta = ventas_por_mes.iloc[0]
|
257 |
-
ultima_venta = ventas_por_mes.iloc[-1]
|
258 |
-
if primera_venta != 0:
|
259 |
-
tasa_crecimiento = ((ultima_venta - primera_venta) / primera_venta) * 100
|
260 |
-
|
261 |
-
tarjetas = [
|
262 |
-
{"titulo": "Total de Ventas", "valor": abreviar_monto(total_ventas), "color": "#4CAF50"},
|
263 |
-
{"titulo": "Promedio de Ventas", "valor": f"${promedio_ventas:,.0f}", "color": "#2196F3"},
|
264 |
-
{"titulo": "Ventas registradas", "valor": total_registros, "color": "#9C27B0"},
|
265 |
-
{"titulo": "Tasa de crecimiento", "valor": f"{tasa_crecimiento:.2f}%" if tasa_crecimiento is not None else "N/A", "color": "#FF5722"},
|
266 |
-
]
|
267 |
-
|
268 |
-
col1, col2, col3, col4 = st.columns(4)
|
269 |
-
cols = [col1, col2, col3, col4]
|
270 |
-
|
271 |
-
for i, tarjeta in enumerate(tarjetas):
|
272 |
-
with cols[i]:
|
273 |
-
st.markdown(f"""
|
274 |
-
<div style='background-color:{tarjeta["color"]}; padding:20px; border-radius:10px; color:white; text-align:center;'>
|
275 |
-
<h4 style='margin:0;'>{tarjeta["titulo"]}</h4>
|
276 |
-
<h2 style='margin:0;'>{tarjeta["valor"]}</h2>
|
277 |
-
</div>
|
278 |
-
""", unsafe_allow_html=True)
|
279 |
-
|
280 |
-
st.markdown("---")
|
281 |
-
|
282 |
-
# Opciones de modelos (incluye una opción por defecto)
|
283 |
-
opciones_modelos = ["(Sin predicción)"] + ["LightGBM", "XGBoost",
|
284 |
-
"HistGradientBoosting",
|
285 |
-
"MLPRegressor", "GradientBoosting",
|
286 |
-
"RandomForest", "CatBoost"]
|
287 |
-
|
288 |
-
col_select, col_plot = st.columns([1, 5])
|
289 |
-
|
290 |
-
with col_select:
|
291 |
-
modelo_seleccionado = st.selectbox("Selecciona un modelo", opciones_modelos)
|
292 |
-
|
293 |
-
with col_plot.container(border=True):
|
294 |
-
if modelo_seleccionado == "(Sin predicción)":
|
295 |
-
if modelo_seleccionado == "(Sin predicción)":
|
296 |
-
df_real = df.copy()
|
297 |
-
df_real = df_real.dropna(subset=["orddt", "sales"])
|
298 |
-
|
299 |
-
fig_real = px.scatter(
|
300 |
-
df_real,
|
301 |
-
x="orddt",
|
302 |
-
y="sales",
|
303 |
-
trendline="ols", # Línea de regresión
|
304 |
-
color_discrete_sequence=["#1f77b4"],
|
305 |
-
trendline_color_override="orange",
|
306 |
-
labels={"sales": "Ventas", "orddt": "Fecha"},
|
307 |
-
title="Ventas Reales (Dispersión + Tendencia)",
|
308 |
-
width=600,
|
309 |
-
height=400
|
310 |
-
)
|
311 |
-
|
312 |
-
fig_real.update_traces(marker=dict(size=6), selector=dict(mode='markers'))
|
313 |
-
fig_real.update_layout(
|
314 |
-
template="plotly_white",
|
315 |
-
margin=dict(l=40, r=40, t=60, b=40),
|
316 |
-
legend_title_text="Datos",
|
317 |
-
showlegend=True
|
318 |
-
)
|
319 |
-
|
320 |
-
st.plotly_chart(fig_real, use_container_width=True)
|
321 |
-
|
322 |
-
else:
|
323 |
-
# Cargar modelo .pkl correspondiente
|
324 |
-
modelo_path = f"regressionmodels/{modelo_seleccionado.lower()}.pkl"
|
325 |
-
modelo = joblib.load(modelo_path)
|
326 |
-
|
327 |
-
# Preparar datos
|
328 |
-
df_pred = df.copy()
|
329 |
-
df_pred = df_pred.dropna(subset=["orddt"])
|
330 |
-
X_nuevo = df_pred.drop(columns=["sales"]) # Asegúrate que coincida con el modelo
|
331 |
-
y_pred = modelo.predict(X_nuevo)
|
332 |
-
df_pred["pred"] = y_pred
|
333 |
-
|
334 |
-
# Calcular precisión del modelo
|
335 |
-
r2 = r2_score(df_pred["sales"], df_pred["pred"])
|
336 |
-
|
337 |
-
# Gráfico de dispersión con línea de regresión
|
338 |
-
fig_pred = px.scatter(
|
339 |
-
df_pred,
|
340 |
-
x="sales",
|
341 |
-
y="pred",
|
342 |
-
trendline="ols",
|
343 |
-
color_discrete_sequence=["#1f77b4"],
|
344 |
-
trendline_color_override="orange",
|
345 |
-
labels={"sales": "Ventas Reales", "pred": "Ventas Predichas"},
|
346 |
-
title=f"Ventas Reales vs Predicción ({modelo_seleccionado})<br><sup>Precisión (R²): {r2:.3f}</sup>",
|
347 |
-
width=600, height=400
|
348 |
-
)
|
349 |
-
fig_pred.update_traces(marker=dict(size=6), selector=dict(mode='markers'))
|
350 |
-
fig_pred.update_layout(
|
351 |
-
legend_title_text='Datos',
|
352 |
-
template="plotly_white",
|
353 |
-
showlegend=True
|
354 |
-
)
|
355 |
-
st.plotly_chart(fig_pred, use_container_width=True)
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
# Fila 1: 3 gráficas
|
360 |
-
col1, col2 = st.columns(2)
|
361 |
-
with col1:
|
362 |
-
with col1.container(border=True):
|
363 |
-
fig1 = px.histogram(df, x='sales', title='Distribución de Ventas',
|
364 |
-
color_discrete_sequence=['#1f77b4'])
|
365 |
-
|
366 |
-
fig1.update_layout(
|
367 |
-
template="plotly_white",
|
368 |
-
margin=dict(l=40, r=40, t=60, b=40),
|
369 |
-
width=600,
|
370 |
-
height=400,
|
371 |
-
legend_title_text="Leyenda"
|
372 |
-
)
|
373 |
-
fig1.update_traces(marker=dict(line=dict(width=0.5, color='white')))
|
374 |
-
|
375 |
-
st.plotly_chart(fig1, use_container_width=True)
|
376 |
-
|
377 |
-
with col2:
|
378 |
-
with col2.container(border=True):
|
379 |
-
fig2 = px.box(df, x='segmt', y='sales', title='Ventas por Segmento',
|
380 |
-
color='segmt', color_discrete_sequence=px.colors.qualitative.Plotly)
|
381 |
-
st.plotly_chart(fig2, use_container_width=True)
|
382 |
-
|
383 |
-
# Fila 2: 2 gráficas
|
384 |
-
col4, col5 = st.columns(2)
|
385 |
-
with col4:
|
386 |
-
with col4.container(border=True):
|
387 |
-
fig4 = px.pie(df, names='categ', values='sales', title='Ventas por Categoría',
|
388 |
-
color_discrete_sequence=px.colors.qualitative.Set3)
|
389 |
-
st.plotly_chart(fig4, use_container_width=True)
|
390 |
-
|
391 |
-
with col5:
|
392 |
-
top_productos = (
|
393 |
-
df.groupby('prdna')['sales']
|
394 |
-
.sum()
|
395 |
-
.sort_values(ascending=False)
|
396 |
-
.head(10)
|
397 |
-
.reset_index()
|
398 |
-
)
|
399 |
-
with col5.container(border=True):
|
400 |
-
fig5 = px.bar(
|
401 |
-
top_productos,
|
402 |
-
x='sales',
|
403 |
-
y='prdna',
|
404 |
-
orientation='h',
|
405 |
-
title='Top 10 productos más vendidos',
|
406 |
-
labels={'sales': 'Ventas', 'prdna': 'Producto'},
|
407 |
-
color='sales',
|
408 |
-
color_continuous_scale='Blues'
|
409 |
-
)
|
410 |
-
|
411 |
-
fig5.update_layout(yaxis={'categoryorder': 'total ascending'})
|
412 |
-
st.plotly_chart(fig5, use_container_width=True)
|
413 |
-
|
414 |
-
col6, col7 = st.columns(2)
|
415 |
-
with col6:
|
416 |
-
with col6.container(border=True):
|
417 |
-
tabla = df.pivot_table(index='state', columns='subct', values='sales', aggfunc='sum').fillna(0)
|
418 |
-
|
419 |
-
if not tabla.empty:
|
420 |
-
tabla = tabla.astype(float)
|
421 |
-
fig6 = px.imshow(
|
422 |
-
tabla.values,
|
423 |
-
labels=dict(x="Categoría", y="Estado", color="Ventas"),
|
424 |
-
x=tabla.columns,
|
425 |
-
y=tabla.index,
|
426 |
-
text_auto=True,
|
427 |
-
title="Mapa de Calor: Ventas por distrito y categoría",
|
428 |
-
color_continuous_scale="Viridis"
|
429 |
-
)
|
430 |
-
st.plotly_chart(fig6, use_container_width=True)
|
431 |
-
else:
|
432 |
-
st.warning("No hay datos suficientes para mostrar el mapa de calor.")
|
433 |
-
|
434 |
-
with col7:
|
435 |
-
ventas_estado = df.groupby('state')['sales'].sum().reset_index()
|
436 |
-
with col7.container(border=True):
|
437 |
-
fig7 = px.bar(ventas_estado, x='state', y='sales', title='Ventas por distrito',
|
438 |
-
color='sales', color_continuous_scale='Teal')
|
439 |
-
st.plotly_chart(fig7, use_container_width=True)
|
440 |
-
|
441 |
-
if st.button("📄 Generar Reporte PDF del Dashboard"):
|
442 |
-
figs = [fig1, fig2, fig4, fig5, fig6, fig7]
|
443 |
-
|
444 |
-
figuras = {}
|
445 |
-
for fig in figs:
|
446 |
-
titulo = fig.layout.title.text or "Sin Título"
|
447 |
-
figuras[titulo] = fig
|
448 |
-
|
449 |
-
st.info("Generando imágenes de las gráficas...")
|
450 |
-
imagenes = guardar_graficas_como_imagen(figuras)
|
451 |
-
st.info("Generando PDF...")
|
452 |
-
ruta_pdf = generar_reporte_dashboard(imagenes)
|
453 |
-
|
454 |
-
with open(ruta_pdf, "rb") as f:
|
455 |
-
st.download_button("⬇️ Descargar Reporte PDF", f, file_name="reporte_dashboard.pdf")
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
def abreviar_monto(valor):
|
460 |
-
if valor >= 1_000_000:
|
461 |
-
return f"${valor / 1_000_000:.2f}M"
|
462 |
-
elif valor >= 1_000:
|
463 |
-
return f"${valor / 1_000:.2f}K"
|
464 |
-
else:
|
465 |
-
return f"${valor:.2f}"
|
466 |
-
|
467 |
-
# -------------------------------
|
468 |
-
# CARGA DE CSV Y GUARDADO EN SESIÓN
|
469 |
-
# -------------------------------
|
470 |
-
|
471 |
-
def loadCSV():
|
472 |
-
columnas_requeridas = [
|
473 |
-
'rowid','ordid','orddt','shpdt',
|
474 |
-
'segmt','state','cono','prodid',
|
475 |
-
'categ','subct','prdna','sales',
|
476 |
-
'order_month','order_day','order_year',
|
477 |
-
'order_dayofweek','shipping_delay'
|
478 |
-
]
|
479 |
-
with st.sidebar.expander("📁 Subir archivo"):
|
480 |
-
uploaded_file = st.file_uploader("Sube un archivo CSV:", type=["csv"], key="upload_csv")
|
481 |
-
|
482 |
-
if uploaded_file is not None:
|
483 |
-
# Reseteamos el estado de 'descargado' cuando se sube un archivo
|
484 |
-
st.session_state.descargado = False
|
485 |
-
st.session_state.archivo_subido = False # Reinicia el estado
|
486 |
-
try:
|
487 |
-
# Leer el archivo subido
|
488 |
-
df = pd.read_csv(uploaded_file)
|
489 |
-
|
490 |
-
# Verificar que las columnas estén presentes y en el orden correcto
|
491 |
-
if list(df.columns) == columnas_requeridas:
|
492 |
-
st.session_state.df_subido = df
|
493 |
-
st.session_state.archivo_subido = True
|
494 |
-
aviso = st.sidebar.success("✅ Archivo subido correctamente.")
|
495 |
-
time.sleep(3)
|
496 |
-
aviso.empty()
|
497 |
-
|
498 |
-
|
499 |
-
else:
|
500 |
-
st.session_state.archivo_subido = False
|
501 |
-
aviso = st.sidebar.error(f"El archivo no tiene las columnas requeridas: {columnas_requeridas}.")
|
502 |
-
time.sleep(3)
|
503 |
-
aviso.empty()
|
504 |
-
|
505 |
-
except Exception as e:
|
506 |
-
aviso = st.sidebar.error(f"Error al procesar el archivo: {str(e)}")
|
507 |
-
time.sleep(3)
|
508 |
-
aviso.empty()
|
509 |
-
|
510 |
-
# -------------------------------
|
511 |
-
# Mostrar uploader y manejar estado
|
512 |
-
# -------------------------------
|
513 |
-
def selectedFile():
|
514 |
-
with st.sidebar.expander("📁 Subir archivo"):
|
515 |
-
uploaded_file = st.file_uploader("Sube un archivo CSV:", type=["csv"], key="upload_csv")
|
516 |
-
|
517 |
-
if uploaded_file is not None:
|
518 |
-
st.session_state.descargado = False
|
519 |
-
st.session_state.archivo_subido = False
|
520 |
-
return uploaded_file
|
521 |
-
return None
|
522 |
-
|
523 |
-
# -------------------------------
|
524 |
-
# Procesar y validar archivo (con cache)
|
525 |
-
# -------------------------------
|
526 |
-
@st.cache_data
|
527 |
-
def loadCSV(uploaded_file):
|
528 |
-
columnas_requeridas = [
|
529 |
-
'rowid','ordid','orddt','shpdt',
|
530 |
-
'segmt','state','cono','prodid',
|
531 |
-
'categ','subct','prdna','sales',
|
532 |
-
'order_month','order_day','order_year',
|
533 |
-
'order_dayofweek','shipping_delay'
|
534 |
-
]
|
535 |
-
|
536 |
-
df = pd.read_csv(uploaded_file)
|
537 |
-
|
538 |
-
if list(df.columns) == columnas_requeridas:
|
539 |
-
return df, None
|
540 |
-
else:
|
541 |
-
return None, f"❌ El archivo no tiene las columnas requeridas: {columnas_requeridas}"
|
542 |
-
|
543 |
-
# -------------------------------
|
544 |
-
# Procesar y validar archivo (con cache)
|
545 |
-
# -------------------------------
|
546 |
-
def verifyFile(uploadedFile):
|
547 |
-
if uploadedFile:
|
548 |
-
try:
|
549 |
-
df, error = loadCSV(uploadedFile)
|
550 |
-
if error is None:
|
551 |
-
st.session_state.df_subido = df
|
552 |
-
st.session_state.archivo_subido = True
|
553 |
-
aviso = st.sidebar.success("✅ Archivo subido correctamente.")
|
554 |
-
else:
|
555 |
-
aviso = st.sidebar.error(error)
|
556 |
-
time.sleep(3)
|
557 |
-
aviso.empty()
|
558 |
-
|
559 |
-
except Exception as e:
|
560 |
-
aviso = st.sidebar.error(f"⚠️ Error al procesar el archivo: {str(e)}")
|
561 |
-
time.sleep(3)
|
562 |
-
aviso.empty()
|
563 |
-
|
564 |
-
# ===========================
|
565 |
-
# Función para descargar archivo CSV
|
566 |
-
# ===========================
|
567 |
-
def downloadCSV(archivo_csv):
|
568 |
-
# Verificamos si el archivo ya ha sido descargado
|
569 |
-
if 'descargado' not in st.session_state:
|
570 |
-
st.session_state.descargado = False
|
571 |
-
|
572 |
-
if not st.session_state.descargado:
|
573 |
-
descarga = st.sidebar.download_button(
|
574 |
-
label="Descargar archivo CSV",
|
575 |
-
data=open(archivo_csv, "rb"),
|
576 |
-
file_name="ventas.csv",
|
577 |
-
mime="text/csv"
|
578 |
-
)
|
579 |
-
if descarga:
|
580 |
-
# Marcamos el archivo como descargado
|
581 |
-
st.session_state.descargado = True
|
582 |
-
aviso = st.sidebar.success("¡Descarga completada!")
|
583 |
-
# Hacer que el mensaje desaparezca después de 2 segundos
|
584 |
-
time.sleep(3)
|
585 |
-
aviso.empty()
|
586 |
-
else:
|
587 |
-
aviso = st.sidebar.success("¡Ya has descargado el archivo!")
|
588 |
-
time.sleep(3)
|
589 |
-
aviso.empty()
|
590 |
-
|
591 |
-
# -------------------------------
|
592 |
-
# FUNCIÓN PARA DETECTAR REFERENCIA AL CSV
|
593 |
-
# -------------------------------
|
594 |
-
def detectedReferenceToCSV(prompt: str) -> bool:
|
595 |
-
palabras_clave = ["csv", "archivo", "contenido cargado", "file", "dataset"]
|
596 |
-
prompt_lower = prompt.lower()
|
597 |
-
return any(palabra in prompt_lower for palabra in palabras_clave)
|
598 |
-
|
599 |
-
# ===========================
|
600 |
-
# Función para interactuar con el bot
|
601 |
-
# ===========================
|
602 |
-
def seleccionar_modelo_llm():
|
603 |
-
modelos_disponibles = {
|
604 |
-
"Alibaba Cloud - Qwen QWQ 32B": "qwen-qwq-32b",
|
605 |
-
"Alibaba Cloud - Qwen3 32B": "qwen/qwen3-32b",
|
606 |
-
"DeepSeek - LLaMA 70B Distill": "deepseek-r1-distill-llama-70b",
|
607 |
-
"Google - Gemma2 9B IT": "gemma2-9b-it",
|
608 |
-
"Meta - LLaMA 3.1 8B Instant": "llama-3.1-8b-instant",
|
609 |
-
"Meta - LLaMA 3.3 70B Versatile": "llama-3.3-70b-versatile",
|
610 |
-
"Meta - LLaMA 3 70B": "llama3-70b-8192",
|
611 |
-
"Meta - LLaMA 3 8B": "llama3-8b-8192",
|
612 |
-
"Meta - LLaMA 4 Maverick 17B": "meta-llama/llama-4-maverick-17b-128e-instruct",
|
613 |
-
"Meta - LLaMA 4 Scout 17B": "meta-llama/llama-4-scout-17b-16e-instruct",
|
614 |
-
"Meta - LLaMA Guard 4 12B": "meta-llama/llama-guard-4-12b",
|
615 |
-
"Meta - Prompt Guard 2 22M": "meta-llama/llama-prompt-guard-2-22m",
|
616 |
-
"Meta - Prompt Guard 2 86M": "meta-llama/llama-prompt-guard-2-86m",
|
617 |
-
"Mistral - Saba 24B": "mistral-saba-24b"
|
618 |
-
}
|
619 |
-
|
620 |
-
seleccion = st.selectbox(
|
621 |
-
"🧠 Elige un modelo LLM de Groq:",
|
622 |
-
list(modelos_disponibles.keys())
|
623 |
-
)
|
624 |
-
|
625 |
-
return modelos_disponibles[seleccion]
|
626 |
-
|
627 |
-
|
628 |
-
def chatBotProtech(client):
|
629 |
-
with st.sidebar.expander("📁 Chatbot"):
|
630 |
-
|
631 |
-
modelo_llm = seleccionar_modelo_llm()
|
632 |
-
|
633 |
-
# Inicializar estados
|
634 |
-
if "chat_history" not in st.session_state:
|
635 |
-
st.session_state.chat_history = []
|
636 |
-
|
637 |
-
if "audio_data" not in st.session_state:
|
638 |
-
st.session_state.audio_data = None
|
639 |
-
|
640 |
-
if "transcripcion" not in st.session_state:
|
641 |
-
st.session_state.transcripcion = ""
|
642 |
-
|
643 |
-
if "mostrar_grabador" not in st.session_state:
|
644 |
-
st.session_state.mostrar_grabador = True
|
645 |
-
|
646 |
-
# Contenedor para mensajes
|
647 |
-
messages = st.container(height=400)
|
648 |
-
|
649 |
-
# CSS: estilo tipo Messenger
|
650 |
-
st.markdown("""
|
651 |
-
<style>
|
652 |
-
.chat-message {
|
653 |
-
display: flex;
|
654 |
-
align-items: flex-start;
|
655 |
-
margin: 10px 0;
|
656 |
-
}
|
657 |
-
.chat-message.user {
|
658 |
-
justify-content: flex-end;
|
659 |
-
}
|
660 |
-
.chat-message.assistant {
|
661 |
-
justify-content: flex-start;
|
662 |
-
}
|
663 |
-
.chat-icon {
|
664 |
-
width: 30px;
|
665 |
-
height: 30px;
|
666 |
-
border-radius: 50%;
|
667 |
-
background-color: #ccc;
|
668 |
-
display: flex;
|
669 |
-
align-items: center;
|
670 |
-
justify-content: center;
|
671 |
-
font-size: 18px;
|
672 |
-
margin: 0 5px;
|
673 |
-
}
|
674 |
-
.chat-bubble {
|
675 |
-
max-width: 70%;
|
676 |
-
padding: 10px 15px;
|
677 |
-
border-radius: 15px;
|
678 |
-
font-size: 14px;
|
679 |
-
line-height: 1.5;
|
680 |
-
word-wrap: break-word;
|
681 |
-
}
|
682 |
-
.chat-bubble.user {
|
683 |
-
background-color: #DCF8C6;
|
684 |
-
color: black;
|
685 |
-
border-top-right-radius: 0;
|
686 |
-
}
|
687 |
-
.chat-bubble.assistant {
|
688 |
-
background-color: #F1F0F0;
|
689 |
-
color: black;
|
690 |
-
border-top-left-radius: 0;
|
691 |
-
}
|
692 |
-
</style>
|
693 |
-
""", unsafe_allow_html=True)
|
694 |
-
|
695 |
-
# Mostrar historial de mensajes
|
696 |
-
with messages:
|
697 |
-
st.header("🤖 ChatBot Protech")
|
698 |
-
for message in st.session_state.chat_history:
|
699 |
-
role = message["role"]
|
700 |
-
content = html.escape(message["content"]) # Escapar contenido HTML
|
701 |
-
bubble_class = "user" if role == "user" else "assistant"
|
702 |
-
icon = "👤" if role == "user" else "🤖"
|
703 |
-
|
704 |
-
# Mostrar el mensaje en una sola burbuja con ícono en el mismo bloque
|
705 |
-
st.markdown(f"""
|
706 |
-
<div class="chat-message {bubble_class}">
|
707 |
-
<div class="chat-icon">{icon}</div>
|
708 |
-
<div class="chat-bubble {bubble_class}">{content}</div>
|
709 |
-
</div>
|
710 |
-
""", unsafe_allow_html=True)
|
711 |
-
|
712 |
-
# --- Manejar transcripción como mensaje automático ---
|
713 |
-
if st.session_state.transcripcion:
|
714 |
-
prompt = st.session_state.transcripcion
|
715 |
-
st.session_state.transcripcion = ""
|
716 |
-
|
717 |
-
st.session_state.chat_history.append({"role": "user", "content": prompt})
|
718 |
-
|
719 |
-
with messages:
|
720 |
-
st.markdown(f"""
|
721 |
-
<div class="chat-message user">
|
722 |
-
<div class="chat-bubble user">{html.escape(prompt)}</div>
|
723 |
-
<div class="chat-icon">👤</div>
|
724 |
-
</div>
|
725 |
-
""", unsafe_allow_html=True)
|
726 |
-
|
727 |
-
with messages:
|
728 |
-
with st.spinner("Pensando..."):
|
729 |
-
completion = callModelLLM(client, prompt, modelo_llm)
|
730 |
-
response = ""
|
731 |
-
response_placeholder = st.empty()
|
732 |
-
|
733 |
-
for chunk in completion:
|
734 |
-
content = chunk.choices[0].delta.content or ""
|
735 |
-
response += content
|
736 |
-
response_placeholder.markdown(f"""
|
737 |
-
<div class="chat-message assistant">
|
738 |
-
<div class="chat-icon">🤖</div>
|
739 |
-
<div class="chat-bubble assistant">{response}</div>
|
740 |
-
</div>
|
741 |
-
""", unsafe_allow_html=True)
|
742 |
-
|
743 |
-
st.session_state.chat_history.append({"role": "assistant", "content": response})
|
744 |
-
|
745 |
-
# Captura del input tipo chat
|
746 |
-
if prompt := st.chat_input("Escribe algo..."):
|
747 |
-
st.session_state.chat_history.append({"role": "user", "content": prompt})
|
748 |
-
|
749 |
-
# Mostrar mensaje del usuario escapado
|
750 |
-
with messages:
|
751 |
-
|
752 |
-
st.markdown(f"""
|
753 |
-
<div class="chat-message user">
|
754 |
-
<div class="chat-bubble user">{prompt}</div>
|
755 |
-
<div class="chat-icon">👤</div>
|
756 |
-
</div>
|
757 |
-
""", unsafe_allow_html=True)
|
758 |
-
|
759 |
-
# Mostrar respuesta del asistente
|
760 |
-
with messages:
|
761 |
-
with st.spinner("Pensando..."):
|
762 |
-
completion = callModelLLM(client, prompt, modelo_llm)
|
763 |
-
response = ""
|
764 |
-
response_placeholder = st.empty()
|
765 |
-
|
766 |
-
for chunk in completion:
|
767 |
-
content = chunk.choices[0].delta.content or ""
|
768 |
-
response += content
|
769 |
-
|
770 |
-
response_placeholder.markdown(f"""
|
771 |
-
<div class="chat-message assistant">
|
772 |
-
<div class="chat-icon">🤖</div>
|
773 |
-
<div class="chat-bubble assistant">{response}</div>
|
774 |
-
</div>
|
775 |
-
""", unsafe_allow_html=True)
|
776 |
-
|
777 |
-
st.session_state.chat_history.append({"role": "assistant", "content": response})
|
778 |
-
|
779 |
-
# Grabación de audio (solo si está habilitada)
|
780 |
-
if st.session_state.mostrar_grabador and st.session_state.audio_data is None:
|
781 |
-
audio_data = st.audio_input("Graba tu voz aquí 🎤")
|
782 |
-
if audio_data:
|
783 |
-
st.session_state.audio_data = audio_data
|
784 |
-
st.session_state.mostrar_grabador = False # Ocultar input después de grabar
|
785 |
-
st.rerun() # Forzar recarga para ocultar input y evitar que reaparezca el audio cargado
|
786 |
-
|
787 |
-
# Mostrar controles solo si hay audio cargado
|
788 |
-
if st.session_state.audio_data:
|
789 |
-
st.audio(st.session_state.audio_data, format="audio/wav")
|
790 |
-
col1, col2 = st.columns(2)
|
791 |
-
|
792 |
-
with col1:
|
793 |
-
if st.button("✅ Aceptar grabación"):
|
794 |
-
with st.spinner("Convirtiendo y transcribiendo..."):
|
795 |
-
m4a_path = converter_bytes_m4a(st.session_state.audio_data)
|
796 |
-
|
797 |
-
with open(m4a_path, "rb") as f:
|
798 |
-
texto = callWhisper(client, m4a_path, f)
|
799 |
-
|
800 |
-
os.remove(m4a_path)
|
801 |
-
|
802 |
-
st.session_state.transcripcion = texto
|
803 |
-
st.session_state.audio_data = None
|
804 |
-
st.session_state.mostrar_grabador = True
|
805 |
-
st.rerun()
|
806 |
-
|
807 |
-
with col2:
|
808 |
-
if st.button("❌ Descartar grabación"):
|
809 |
-
st.session_state.audio_data = None
|
810 |
-
st.session_state.transcripcion = ""
|
811 |
-
st.session_state.mostrar_grabador = True
|
812 |
-
st.rerun()
|
813 |
-
|
814 |
-
|
815 |
-
def callModelLLM(client, prompt, idModel):
|
816 |
-
completion = client.chat.completions.create(
|
817 |
-
model=idModel,
|
818 |
-
messages=[
|
819 |
-
{
|
820 |
-
"role": "system",
|
821 |
-
"content": (
|
822 |
-
"Tu nombre es Protech, el asistente virtual de PRO TECHNOLOGY SOLUTIONS S.A.C. "
|
823 |
-
"Saluda al usuario con cordialidad y responde en español de forma clara, profesional y amable. "
|
824 |
-
"Debes responder como un asistente humano capacitado en atención al cliente. "
|
825 |
-
"Comienza con un saludo y pregunta: '¿En qué puedo ayudarte hoy?'."
|
826 |
-
)
|
827 |
-
},
|
828 |
-
{"role": "user", "content": prompt}
|
829 |
-
],
|
830 |
-
temperature=0.6,
|
831 |
-
max_tokens=4096,
|
832 |
-
top_p=1,
|
833 |
-
stream=True,
|
834 |
-
)
|
835 |
-
return completion
|
836 |
-
|
837 |
-
def callWhisper(client, filename_audio,file):
|
838 |
-
transcription = client.audio.transcriptions.create(
|
839 |
-
file=(filename_audio, file.read()),
|
840 |
-
model="whisper-large-v3",
|
841 |
-
response_format="verbose_json",
|
842 |
-
)
|
843 |
-
return transcription.text
|
844 |
-
|
845 |
-
def converter_bytes_m4a(audio_bytes: BytesIO) -> str:
|
846 |
-
"""
|
847 |
-
Convierte un audio en bytes (WAV, etc.) a un archivo M4A temporal.
|
848 |
-
Retorna la ruta del archivo .m4a temporal.
|
849 |
-
"""
|
850 |
-
# Asegurarse de que el cursor del stream esté al inicio
|
851 |
-
audio_bytes.seek(0)
|
852 |
-
|
853 |
-
# Leer el audio desde BytesIO usando pydub
|
854 |
-
audio = AudioSegment.from_file(audio_bytes)
|
855 |
-
|
856 |
-
# Crear archivo temporal para guardar como .m4a
|
857 |
-
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".m4a")
|
858 |
-
m4a_path = temp_file.name
|
859 |
-
temp_file.close() # Cerramos para que pydub pueda escribirlo
|
860 |
-
|
861 |
-
# Exportar a M4A usando formato compatible con ffmpeg
|
862 |
-
audio.export(m4a_path, format="ipod") # 'ipod' genera .m4a
|
863 |
-
|
864 |
-
return m4a_path
|
865 |
-
|
866 |
-
# ===========================
|
867 |
-
# Función para obtener el número de periodos basado en el filtro
|
868 |
-
# ===========================
|
869 |
-
def obtener_periodos(filtro):
|
870 |
-
opciones_periodos = {
|
871 |
-
'5 años': 60,
|
872 |
-
'3 años': 36,
|
873 |
-
'1 año': 12,
|
874 |
-
'5 meses': 5
|
875 |
-
}
|
876 |
-
return opciones_periodos.get(filtro, 12)
|
877 |
-
|
878 |
-
|
879 |
-
# ===========================
|
880 |
-
# Función para generar predicciones
|
881 |
-
# ===========================
|
882 |
-
def generar_predicciones(modelo, df, periodos):
|
883 |
-
ventas = df['Sale']
|
884 |
-
predicciones = modelo.forecast(steps=periodos)
|
885 |
-
return predicciones
|
886 |
-
|
887 |
-
# Función para mejorar el diseño de las gráficas
|
888 |
-
def mejorar_diseno_grafica(fig, meses_relevantes, nombres_meses_relevantes):
|
889 |
-
fig.update_layout(
|
890 |
-
title={
|
891 |
-
'text': "Ventas vs Mes",
|
892 |
-
|
893 |
-
'x': 0.5, # Centrado horizontal
|
894 |
-
'xanchor': 'center', # Asegura el anclaje central
|
895 |
-
'yanchor': 'top' # Anclaje superior (opcional)
|
896 |
-
},
|
897 |
-
title_font=dict(size=18, family="Arial, sans-serif", color='black'),
|
898 |
-
xaxis=dict(
|
899 |
-
title='Mes-Año',
|
900 |
-
title_font=dict(size=14, family="Arial, sans-serif", color='black'),
|
901 |
-
tickangle=-45, # Rotar las etiquetas
|
902 |
-
showgrid=True,
|
903 |
-
gridwidth=0.5,
|
904 |
-
gridcolor='lightgrey',
|
905 |
-
showline=True,
|
906 |
-
linecolor='black',
|
907 |
-
linewidth=2,
|
908 |
-
tickmode='array', # Controla qué etiquetas mostrar
|
909 |
-
tickvals=meses_relevantes, # Selecciona solo los meses relevantes
|
910 |
-
ticktext=nombres_meses_relevantes, # Meses seleccionados
|
911 |
-
tickfont=dict(size=10), # Reducir el tamaño de la fuente de las etiquetas
|
912 |
-
),
|
913 |
-
yaxis=dict(
|
914 |
-
title='Ventas',
|
915 |
-
title_font=dict(size=14, family="Arial, sans-serif", color='black'),
|
916 |
-
showgrid=True,
|
917 |
-
gridwidth=0.5,
|
918 |
-
gridcolor='lightgrey',
|
919 |
-
showline=True,
|
920 |
-
linecolor='black',
|
921 |
-
linewidth=2
|
922 |
-
),
|
923 |
-
plot_bgcolor='white', # Fondo blanco
|
924 |
-
paper_bgcolor='white', # Fondo del lienzo de la gráfica
|
925 |
-
font=dict(family="Arial, sans-serif", size=12, color="black"),
|
926 |
-
showlegend=False, # Desactivar la leyenda si no es necesaria
|
927 |
-
margin=dict(l=50, r=50, t=50, b=50) # Márgenes ajustados
|
928 |
-
)
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
return fig
|
933 |
-
|
934 |
-
# ===========================
|
935 |
-
# Función para cerrar sesión
|
936 |
-
# ===========================
|
937 |
-
def cerrar_sesion():
|
938 |
-
st.session_state.logged_in = False
|
939 |
-
st.session_state.usuario = None
|
940 |
-
st.session_state.pagina_actual = "login"
|
941 |
-
st.session_state.archivo_subido = False # Limpiar el archivo subido al cerrar sesión
|
942 |
-
st.session_state.df_subido = None # Limpiar datos del archivo
|
943 |
-
# Eliminar parámetros de la URL usando st.query_params
|
944 |
-
st.query_params.clear() # Método correcto para limpiar parámetros de consulta
|
945 |
-
|
946 |
-
# Redirigir a la página de login
|
947 |
-
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
paginas/demo.py
DELETED
@@ -1,27 +0,0 @@
|
|
1 |
-
from base64 import b64encode
|
2 |
-
from fpdf import FPDF
|
3 |
-
import streamlit as st
|
4 |
-
|
5 |
-
st.title("Demo of fpdf2 usage with streamlit")
|
6 |
-
|
7 |
-
@st.cache_data
|
8 |
-
def gen_pdf():
|
9 |
-
pdf = FPDF()
|
10 |
-
pdf.add_page()
|
11 |
-
pdf.set_font("Helvetica", size=24)
|
12 |
-
pdf.cell(w=40,h=10,border=1,txt="hello world")
|
13 |
-
return pdf.output(dest='S').encode('latin1')
|
14 |
-
|
15 |
-
|
16 |
-
# Embed PDF to display it:
|
17 |
-
base64_pdf = b64encode(gen_pdf()).decode("utf-8")
|
18 |
-
pdf_display = f'<embed src="data:application/pdf;base64,{base64_pdf}" width="700" height="400" type="application/pdf">'
|
19 |
-
st.markdown(pdf_display, unsafe_allow_html=True)
|
20 |
-
|
21 |
-
# Add a download button:
|
22 |
-
st.download_button(
|
23 |
-
label="Download PDF",
|
24 |
-
data=gen_pdf(),
|
25 |
-
file_name="file_name.pdf",
|
26 |
-
mime="application/pdf",
|
27 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
paginas/demokaleido.py
DELETED
@@ -1,5 +0,0 @@
|
|
1 |
-
import plotly.express as px
|
2 |
-
|
3 |
-
fig = px.line(x=[1, 2, 3], y=[4, 5, 6])
|
4 |
-
fig.write_image("test_fig.png", width=900, height=500)
|
5 |
-
print("✅ Imagen guardada correctamente")
|
|
|
|
|
|
|
|
|
|
|
|
paginas/images/Logo dashboard.png
DELETED
Git LFS Details
|
paginas/images/Logo general.png
DELETED
Binary file (1.93 kB)
|
|
paginas/images/Logo.png
DELETED
Binary file (1.8 kB)
|
|
paginas/login.py
DELETED
@@ -1,59 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import pandas as pd
|
3 |
-
import json
|
4 |
-
from streamlit_lottie import st_lottie
|
5 |
-
import os
|
6 |
-
import time
|
7 |
-
from .userManagement import verifyCredentials
|
8 |
-
|
9 |
-
def validateCredentials(usuario, contrasena):
|
10 |
-
return verifyCredentials(usuario, contrasena)
|
11 |
-
|
12 |
-
def load_lottiefile(filepath: str):
|
13 |
-
with open(filepath, "r") as f:
|
14 |
-
return json.load(f)
|
15 |
-
|
16 |
-
|
17 |
-
def showLogin():
|
18 |
-
|
19 |
-
c1, c2 = st.columns([60, 40])
|
20 |
-
|
21 |
-
with c1:
|
22 |
-
# Ajusta la ruta al archivo JSON de animación
|
23 |
-
ruta_animacion_laptop = os.path.join("animations", "laptopUser.json")
|
24 |
-
lottie_coding = load_lottiefile(ruta_animacion_laptop)
|
25 |
-
st_lottie(
|
26 |
-
lottie_coding,
|
27 |
-
speed=1,
|
28 |
-
reverse=False,
|
29 |
-
loop=True,
|
30 |
-
quality="low",
|
31 |
-
height=None,
|
32 |
-
width=None,
|
33 |
-
key=None,
|
34 |
-
)
|
35 |
-
|
36 |
-
with c2:
|
37 |
-
st.title("🔐 Inicio de :blue[Sesión] :sunglasses:")
|
38 |
-
|
39 |
-
# Formulario de inicio de sesión
|
40 |
-
with st.form("login_form"):
|
41 |
-
usuario = st.text_input("Usuario 👇")
|
42 |
-
contrasena = st.text_input("Contraseña 👇", type="password")
|
43 |
-
boton_login = st.form_submit_button("Iniciar Sesión", type="primary",use_container_width=True)
|
44 |
-
|
45 |
-
# Validación de credenciales
|
46 |
-
if boton_login:
|
47 |
-
if validateCredentials(usuario, contrasena):
|
48 |
-
st.session_state.logged_in = True
|
49 |
-
st.session_state.usuario = usuario
|
50 |
-
aviso = st.success("Inicio de sesión exitoso. Redirigiendo al dashboard...")
|
51 |
-
time.sleep(3)
|
52 |
-
aviso.empty()
|
53 |
-
# Simular redirección recargando el flujo principal
|
54 |
-
st.session_state.pagina_actual = "dashboard"
|
55 |
-
st.rerun()
|
56 |
-
else:
|
57 |
-
aviso = st.error("Usuario o contraseña incorrectos")
|
58 |
-
time.sleep(3)
|
59 |
-
aviso.empty()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
paginas/userManagement.py
DELETED
@@ -1,32 +0,0 @@
|
|
1 |
-
import MySQLdb
|
2 |
-
from .conexionMysql import get_db_connection
|
3 |
-
|
4 |
-
def verifyCredentials(username: str, password: str) -> bool:
|
5 |
-
"""
|
6 |
-
Verifica si las credenciales del usuario son válidas.
|
7 |
-
Retorna True si el usuario existe y la contraseña es correcta.
|
8 |
-
"""
|
9 |
-
try:
|
10 |
-
with get_db_connection() as conn:
|
11 |
-
cursor = conn.cursor()
|
12 |
-
query = "SELECT COUNT(*) FROM usuarios WHERE username = %s AND password = %s"
|
13 |
-
cursor.execute(query, (username, password))
|
14 |
-
resultado = cursor.fetchone()
|
15 |
-
return resultado[0] > 0
|
16 |
-
except MySQLdb.Error as e:
|
17 |
-
print(f"Error en la verificación de credenciales: {e}")
|
18 |
-
return False
|
19 |
-
|
20 |
-
def getDataUser(correo: str) -> dict | None:
|
21 |
-
"""
|
22 |
-
Devuelve un diccionario con los datos del usuario si existe, o None si no se encuentra.
|
23 |
-
"""
|
24 |
-
try:
|
25 |
-
with get_db_connection() as conn:
|
26 |
-
cursor = conn.cursor(MySQLdb.cursors.DictCursor)
|
27 |
-
query = "SELECT nombre, apellido, correo, telefono FROM usuarios WHERE correo = %s"
|
28 |
-
cursor.execute(query, (correo,))
|
29 |
-
return cursor.fetchone()
|
30 |
-
except MySQLdb.Error as e:
|
31 |
-
print(f"Error al obtener datos del usuario: {e}")
|
32 |
-
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
randomforest.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:8ec6cfc13fafd2835935508ad2778f15ee5c6c54eee26caca9299e5710ec24b6
|
3 |
-
size 26262394
|
|
|
|
|
|
|
|
regressionmodels/catboost.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:059f80be82f0f5ace74dc7ee09cd8a24502bb2e106623f43404a65c1df9abd0f
|
3 |
-
size 1115713
|
|
|
|
|
|
|
|
regressionmodels/gradientboosting.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:5bb28622f5a612db81f8c5d12f56913710303da5778551e6931dd97393ebad1c
|
3 |
-
size 145876
|
|
|
|
|
|
|
|
regressionmodels/histgradientboosting.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:13dca14204a9cbdd7bb407ae68697b9d4acc5b4969119b5abd2301f8c6e674cd
|
3 |
-
size 375945
|
|
|
|
|
|
|
|
regressionmodels/lightgbm.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:ae50d6dfc075ff1b930fcb207169710ab452f384a006ed4a3c61e3d2baae35f5
|
3 |
-
size 281459
|
|
|
|
|
|
|
|
regressionmodels/mlpregressor.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:287880615fc3e4cfedbba56d537bb7217ea86808ed896d81fd75b43f3b596387
|
3 |
-
size 261329
|
|
|
|
|
|
|
|
regressionmodels/randomforest.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:8ec6cfc13fafd2835935508ad2778f15ee5c6c54eee26caca9299e5710ec24b6
|
3 |
-
size 26262394
|
|
|
|
|
|
|
|
regressionmodels/xgboost.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:31639da06ebc5667ef2fffb043e3acb0ecea1294ffa6dd1dd0df3c6ee772d36c
|
3 |
-
size 358669
|
|
|
|
|
|
|
|
requirements.txt
DELETED
Binary file (5 kB)
|
|
xgboost.pkl
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:31639da06ebc5667ef2fffb043e3acb0ecea1294ffa6dd1dd0df3c6ee772d36c
|
3 |
-
size 358669
|
|
|
|
|
|
|
|