actualizado 3-sept
@@ -9,7 +9,9 @@ COPY . .
|
|||||||
|
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
|
||||||
CMD ["gunicorn", "-b", "0.0.0.0:5000", "app:app"]
|
CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "app:app"]
|
||||||
|
|
||||||
|
# CMD ["gunicorn", "-b", "0.0.0.0:5000", "app:app"]
|
||||||
|
# gunicorn -w 2 -b 0.0.0.0:5000 app:app
|
||||||
# CMD ["python", "app.py"]
|
# CMD ["python", "app.py"]
|
||||||
# docker compose up -d --build
|
# docker compose up -d --build
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
## License
|
||||||
|
|
||||||
|
Copyright (C) Max Geissler
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# adminer-sqlite
|
||||||
|
|
||||||
|
With adminer-sqlite you can use [adminer](https://www.adminer.org) with a local [sqlite](https://sqlite.org) database.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Hint: The following usage instructions are written for macOS and Linux. On Windows, the commands must be adapted.
|
||||||
|
|
||||||
|
1. Download and install [Docker](https://www.docker.com/).
|
||||||
|
|
||||||
|
2. Download adminer-sqlite
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/fluidblue/adminer-sqlite.git
|
||||||
|
cd adminer-sqlite
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Copy your database to `db` directory -or- create a symbolic link to the database
|
||||||
|
|
||||||
|
```
|
||||||
|
# Option 1
|
||||||
|
cp /path/to/db.sqlite3 ./db/db.sqlite3
|
||||||
|
```
|
||||||
|
|
||||||
|
# Option 2
|
||||||
|
|
||||||
|
ln -s /path/to/db.sqlite3 ./db/db.sqlite3
|
||||||
|
|
||||||
|
```
|
||||||
|
4. Set a new password in `docker-compose.yml` (environment variable `ADMINER_PASSWORD`).
|
||||||
|
Note: You can't use an empty password.
|
||||||
|
|
||||||
|
5. Use Docker compose to start the container
|
||||||
|
```
|
||||||
|
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
```
|
||||||
|
6. Go to http://localhost:8080 and enter the following
|
||||||
|
```
|
||||||
|
|
||||||
|
System: SQLite 3
|
||||||
|
Username: <empty>
|
||||||
|
Password: <your password>
|
||||||
|
Database: /db/db.sqlite3
|
||||||
|
|
||||||
|
```
|
||||||
|
Para acceder a nexus:
|
||||||
|
```
|
||||||
|
|
||||||
|
System: SQLite 3
|
||||||
|
Username: <empty>
|
||||||
|
Password: p4ssw0rd
|
||||||
|
Database: /db/pyfleet.db
|
||||||
|
|
||||||
|
```
|
||||||
|
La ruta Database es la ruta dentro del contenedor, no la ruta del host.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
|
||||||
|
Copyright (C) 2023 Max Geissler
|
||||||
|
|
||||||
|
This program is free software, licensed under the GNU General Public License (GPL) v3.0 or later.
|
||||||
|
Please see the [License](LICENSE.md) for further information.
|
||||||
|
```
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
Theme by Hever [http://hev.cz] - September 2019, ver 0.2.5
|
||||||
|
Used Silk icon set 1.3 by Mark James - http://www.famfamfam.com/lab/icons/silk
|
||||||
|
*/
|
||||||
|
|
||||||
|
.error {background:#FFEEEE url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIsSURBVDjLpVNLSJQBEP7+h6uu62vLVAJDW1KQTMrINQ1vPQzq1GOpa9EppGOHLh0kCEKL7JBEhVCHihAsESyJiE4FWShGRmauu7KYiv6Pma+DGoFrBQ7MzGFmPr5vmDFIYj1mr1WYfrHPovA9VVOqbC7e/1rS9ZlrAVDYHig5WB0oPtBI0TNrUiC5yhP9jeF4X8NPcWfopoY48XT39PjjXeF0vWkZqOjd7LJYrmGasHPCCJbHwhS9/F8M4s8baid764Xi0Ilfp5voorpJfn2wwx/r3l77TwZUvR+qajXVn8PnvocYfXYH6k2ioOaCpaIdf11ivDcayyiMVudsOYqFb60gARJYHG9DbqQFmSVNjaO3K2NpAeK90ZCqtgcrjkP9aUCXp0moetDFEeRXnYCKXhm+uTW0CkBFu4JlxzZkFlbASz4CQGQVBFeEwZm8geyiMuRVntzsL3oXV+YMkvjRsydC1U+lhwZsWXgHb+oWVAEzIwvzyVlk5igsi7DymmHlHsFQR50rjl+981Jy1Fw6Gu0ObTtnU+cgs28AKgDiy+Awpj5OACBAhZ/qh2HOo6i+NeA73jUAML4/qWux8mt6NjW1w599CS9xb0mSEqQBEDAtwqALUmBaG5FV3oYPnTHMjAwetlWksyByaukxQg2wQ9FlccaK/OXA3/uAEUDp3rNIDQ1ctSk6kHh1/jRFoaL4M4snEMeD73gQx4M4PsT1IZ5AfYH68tZY7zv/ApRMY9mnuVMvAAAAAElFTkSuQmCC") no-repeat scroll 0.8em center; padding-left:38px;}
|
||||||
|
.message, #menu p.message {background:#EEFFEE url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKfSURBVDjLpZPrS1NhHMf9O3bOdmwDCWREIYKEUHsVJBI7mg3FvCxL09290jZj2EyLMnJexkgpLbPUanNOberU5taUMnHZUULMvelCtWF0sW/n7MVMEiN64AsPD8/n83uucQDi/id/DBT4Dolypw/qsz0pTMbj/WHpiDgsdSUyUmeiPt2+V7SrIM+bSss8ySGdR4abQQv6lrui6VxsRonrGCS9VEjSQ9E7CtiqdOZ4UuTqnBHO1X7YXl6Daa4yGq7vWO1D40wVDtj4kWQbn94myPGkCDPdSesczE2sCZShwl8CzcwZ6NiUs6n2nYX99T1cnKqA2EKui6+TwphA5k4yqMayopU5mANV3lNQTBdCMVUA9VQh3GuDMHiVcLCS3J4jSLhCGmKCjBEx0xlshjXYhApfMZRP5CyYD+UkG08+xt+4wLVQZA1tzxthm2tEfD3JxARH7QkbD1ZuozaggdZbxK5kAIsf5qGaKMTY2lAU/rH5HW3PLsEwUYy+YCcERmIjJpDcpzb6l7th9KtQ69fi09ePUej9l7cx2DJbD7UrG3r3afQHOyCo+V3QQzE35pvQvnAZukk5zL5qRL59jsKbPzdheXoBZc4saFhBS6AO7V4zqCpiawuptwQG+UAa7Ct3UT0hh9p9EnXT5Vh6t4C22QaUDh6HwnECOmcO7K+6kW49DKqS2DrEZCtfuI+9GrNHg4fMHVSO5kE7nAPVkAxKBxcOzsajpS4Yh4ohUPPWKTUh3PaQEptIOr6BiJjcZXCwktaAGfrRIpwblqOV3YKdhfXOIvBLeREWpnd8ynsaSJoyESFphwTtfjN6X1jRO2+FxWtCWksqBApeiFIR9K6fiTpPiigDoadqCEag5YUFKl6Yrciw0VOlhOivv/Ff8wtn0KzlebrUYwAAAABJRU5ErkJggg==") no-repeat scroll 0.8em center; padding-left:38px;}
|
||||||
|
|
||||||
|
#dbs span, th a[href*="&db="]:not([href*="&select="]):not([href*="&table="]) {background:transparent url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEYSURBVBgZBcHPio5hGAfg6/2+R980k6wmJgsJ5U/ZOAqbSc2GnXOwUg7BESgLUeIQ1GSjLFnMwsKGGg1qxJRmPM97/1zXFAAAAEADdlfZzr26miup2svnelq7d2aYgt3rebl585wN6+K3I1/9fJe7O/uIePP2SypJkiRJ0vMhr55FLCA3zgIAOK9uQ4MS361ZOSX+OrTvkgINSjS/HIvhjxNNFGgQsbSmabohKDNoUGLohsls6BaiQIMSs2FYmnXdUsygQYmumy3Nhi6igwalDEOJEjPKP7CA2aFNK8Bkyy3fdNCg7r9/fW3jgpVJbDmy5+PB2IYp4MXFelQ7izPrhkPHB+P5/PjhD5gCgCenx+VR/dODEwD+A3T7nqbxwf1HAAAAAElFTkSuQmCC") no-repeat scroll left bottom; padding-left:22px;}
|
||||||
|
|
||||||
|
.links a {margin-right:8px;}
|
||||||
|
|
||||||
|
a[href$="&sql="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHGSURBVHjaxFPNLkNREP5aB6WhaKLSVtKFiIUqK1sLT2DhTXgDL2FlxSOw8FOLRiyQWhDRiKSEhKQJou49P9fMnFsLsevCJCcz594z3/m+mTmJKIrQjSXRpXUNoGqbm39qcMbAkbyIvbVwYQhD3tIeWsOQ1+QVH86Xyz+JXJPIOb9iAI4ZpOMd/yN/vb/vAdiC93cP0El2dNA6z4RjYyW2MaPU0BB0u+0BOGGsVML49LSA3J+cYDCblb0l6jeHBwg/26isrOB0a8uzYBlBgKShgD8M53J4aTTQqFaRSCYl+WxnG83zcxTmKpKg+vtFu9W8NDQDMCXW+VivYyAzjKmlJahUSpiYUKPVbCKTz0sCGzOyOpBLzdcXlCMdDDIxO4vboyoyhQIKlYocLi0uIjczIyBcA98dYqx9NywxUEyDi/P29IyF1VU5dFeroS+dRnF+Xvajk5N0ayjx8tq6+FuSyiwUF4LRHi/reLg4o9ijs6xG9RjWGowUi/h4fcXexoa0L4oc0mNZuVgFzID09VKBnFXoUb7Pnb5zQrvVovpkZC4QzwfiN6QM1eBqdxcmnq6IAA395Mlz8eTxTZwg/pcl/v01fgswAESqYZbsIsnLAAAAAElFTkSuQmCC") no-repeat scroll left bottom; padding-left:22px;}
|
||||||
|
a[href*="&dump="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJrSURBVHjajFPPaxNREP72V8ymTbGQbRB/IT0otdV6CcWTgqCoUFQQEXoSEQ/+AypK755E1KsULIpGgw1F9KC22lgstFgQLdoWhSab2PRHTNrs7nvObGhSsRa/ZXjv7c6b+eabWUVKiX9AJQuRRci2k+260N91VwgJIQSEV1mVnjf5axTjvCfFFn7hCcBlBzJNVRA0gAZTQ1ODgXC4gDtjV9AW2QNFUTA0/Q66kPLSqVg4shYF8vEdKQ7mln+i+/VVRM0oFpbmETLq/SS6J4R/OfHtHmV24XouHFo94REDAyE9hMZgBMkvT2GZFqxQE6by0/g4P74SoKLB2ZZzWA8ffqRQLBcxNTuFjRTwxpmbfad7Oo/rHhdN6B20/6JvaKyBCnODhqNbL+PRxHXUGXWINV5kl9TDrkQct/pnZKHkyPsDGcl4MJiWq5Ecsf84LxTL8nbyK2+Pke3XHWJAQkLXgMdDNjRNRTxlQ6UmakRD0vN8NEd7EBsFO6Impu1fzGCCrOSXwCro5HEiZiExnEVnrKlaypNhG4fba02aTC8ik1/ibZrM9RlIkkGnjH0jWWJQWRn8TpLI8fcZ6MSA1WrZFERx2eHPTEPqjksBqASDLh7ZZ+HlWA6H9tYyvhjNomNnI8Km7p8/f5+HGdB46/lJeOoYPHV8mbGy8gA59HngU74asNkKYKVzlQCuW9GA6B5si+DVeA4HWmsM4qksTnZY1TMz0NRauysaUAmt2+oxM1tCM6k8mS74WXjIdm8O+JdWg36oKgXqgnzb3TvRXvZEneN6YPNIFw7MY70W5haXnlUHbp3f+b/wW4ABAAtWTLcKdqLcAAAAAElFTkSuQmCC") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
a[href$="&dump="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHzSURBVHjajJPPS+NQEMcnaaxpdWsp6Q8vtWtdkIo9+B948SjIgruCUg8F/wGP/glePe5JcBehwl4qePGm0J6KQj30UBHEzaFLUromTfPizJMnrxhhB4bJvDfzme97SZQgCOB/be98J2AsAMYYMP81KjKg0WhMYljHtS30RSz4gjkGdot559F7OLt8vvi1bJRBURS4vr8CTTQ3m80SFv1MpVJlwzBA13XuNGUwGKyY1p+Vk9aPzWw8C7ZjQXximu+9ATA5KhQK5Uwmw+nCIpEIJBIJOLjah3Q8w7379x5urFsOUEUhTl/C6R+ef35mARzPhW6vC+q/iHvyvaYwXwIQje6j1+tBv98H13VhNBpxxyPA5tw2TI50MPQsrMbXbOo53fmtaJIC7iSZAMPhkOe+73MA5Ruz3yCfz9NlvykLBUSjUR41TQPP8zhE7JNSyj8E0CbJpkiXKRoIRFABeQcQd0DFwlVV5evUTFBZRSiATEwnp4lCAa0LI2AoQC4WimRl4qgyYOw12rYNyWSSX6L4mKiBnukDK5VKYJrmmBpZwW6r1TotFotTuVyOQwQgnU7zV9npdKDdbj+hgnXRN/Yz1ev1zwg6QP+KU2disRiXblkWOI7zhJOPce+wWq2aoQDZarXaJ2xYQGfod5VKxQ2rexFgAI4OiAKxKkWeAAAAAElFTkSuQmCC") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
a[href*="&import="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gEJFDQPlDJUDwAAAiNJREFUOMutk0tI1FEUh7//Y9TJghyc1KQ20lMthRAlgoKgVkWte6wid0EICUHUQjdBErRqHYmQghgU5KLQGB8FKVEMCi0G1JmRbPLJ/O89p8XYOAMZBB24HM5dfL/fOfdcB6Aj1j6kqm2qWv/o5NMk/xBOR6z9dVVpzbmQEyKxPMcBc5svbheHs91YK1hRjJWCY7FGsSKT3VcPtviquqaqVJVVY6xlZu0hYpRrpyJ/Vb7fN3sMwAG4965Hw+ElKrzdlPphPqTHaYw0EdiAQEwuW4MRg7GGzrYu7j6L8/hGg+MD7DNXmF7tpDpSg6MOYpWbzbe2VV/dMATGAuADTLt3UFEmU+OICCpK72hqW8CFExGymwAAnrya15X1QJ+PJFVVtW90QQvjRay4TmfW9XrPpOYdBFYQVXwP+mMpPM9lYCyF44K7KTI4kQJVQj4cqd1BNihowVpBAd9zuNQSZXAizcWWPXmH/WPJonp27ifGCEUOVMB34eXHNJ6Xy5C7+w3xHRDg6N4ylEKAEVSVkAfnm6MMTy1y9nhlXvHNpzSthyrYFfYBiCcylJf6WwAjOZrnOgxPLQLks+tAIDDydSkPrIuWYG2BA2NMbgYunGms5O3nRU43bDkYGEtzuTWar+OJDN5ma1szUKVh/07mv69TVxXm28JK/i/U15YQT2SKdkFEpeAV9P2D3pmmrJXywFgCY7FGCKxgrPnjMv1Y3hjif8QvwdlGGi2XXTgAAAAASUVORK5CYII=") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
a[href$="&import="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gEJFDMETKEbQAAAAjZJREFUOMuVkjFoU2EUhc//3gsvptFCIdRHh2CrQ1AcmsUYHTKJDkJrFUdx6uJkpuLspCI4xaJLNik4SMGlOKgEdekQ0EEwFhJqCw+a16RJ//+/x0Heo6XN4B0v537nHLgKAKqNxXckSyTPPyu//IP/GFVtLL6f9INrKZXC791f3UvDysPP3tryjcHt7yTPkRQRaZL8KSKvRGRtbm7OxgCPZJ8kJtOnYaw99ZUfl8UQ5XK5kMlkQBI7OzuzURTNbmxs3Nne3v60srJyd2FhoQ0AztPLtflOv/22tdtCVo3hwvhFUATZbBaO48B1XUxMTCCfz6NUKmF6evqKtfZxnMABgOdXl+c7vTZ87wQUFcRyZOepqSmIyPWkAgA8+HCfFMG3rS8QEVCIvb09pFIpkES/30cYhmi325iZmYGI4BDgReW1Wl1d3apUKrler4coitBqtUASxhh0u10Mh0N4ngeSRwEAQBIk4bou0uk0PM+D53nQWsMYA/JfLRGBtfYoQEQSx/hIKQVrbbJzHGd0ghgQHxhj4LourLXQWkNrDdd1E81IQCy21iaAOEFcU2t9PEBEEvcYeDBZrNvf38ehPwAAay3CMEQul4Pv+4f/XSkEQYBisYhOpzMywb1Go/GmUCiM5fN5+L4PpRRIIggCRFGEZrOJ9fX1TWPMzQR+0Kler58RkUcicstxnPFMJgNjDMIwxGAw2DTG1K21T5aWlraOBRycWq120hhz1hgjWusf1Wp1eJzuL3uxkTGLHB98AAAAAElFTkSuQmCC") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
|
||||||
|
@media all and (min-width: 881px) {
|
||||||
|
#menu .links {height:22px; transition:.2s;}
|
||||||
|
#menu .links:hover {height:5em;}
|
||||||
|
#menu .links a {color:transparent; transition:.2s; display:block; margin-bottom:-1.25em;}
|
||||||
|
#menu .links > a + a {margin-left:22px;}
|
||||||
|
#menu .links > a + a + a {margin-left:44px;}
|
||||||
|
#menu .links > a + a + a + a {margin-left:66px;}
|
||||||
|
#menu .links:hover a {color:blue; margin:0;}
|
||||||
|
#menu .links a:hover {color:red;}
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu ul#tables a[href*="&select="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHISURBVDjLpVPNK0RRFP+9D98syMwUspHkm9I0YkFZWBFKkZ0s7a3Ewh+ilChK7FgoZCJFKYlYKB8zk2+Z5t0P577He29kQU7dd+6575zf+d1zztWklPiPmOozt/U4SThjXIoyIQS4AJjSXO0lGGlvcXAm6Vzsz4xUhm0AIeX4QLig+C+ZpxbOG1wGhGYHr1zMUmZGWRgs0ha3PE1nX/8mWmdgWTzLB+DUYbhm9FfZ35IEyrhXA3VXJfPbsV8B9LQUIeUHYJ8ASobag1jcucNgW8g9W4reYSDi2YnnZDoDiwCokDANct6NwTB0LEdj0HRA/wxa2SN25JNBEdWluUhZ366gqmAaGvrCAXKOozccTGPgt8+vn8GYSGcgyTYp3dpBnBg42nbQPRBTo5bTvqYkmxL6AQhNTWQGBXY3B7BxlEBXozcW64dxRKoKUZBju+P06gl5WaaviMJBM3TNDlbypemIZgHYOnlwASsCmW7nHADGnBoQ3c76YmweJ9BR5zFYjsbRHwm4tmJg6PhWA7pCXXk+bu7fURHKweXtq/sWaksz7SC/CCGFrwtyZ3r+rCnFRZ7qr1qc6mLZj4f9OEyPL8lVpbX/PucPv5QPKHB1TdEAAAAASUVORK5CYII=") no-repeat scroll right bottom; display:inline-block; height:16px; margin-right:4px; vertical-align:middle; overflow:hidden; padding-left:16px; width:0;}
|
||||||
|
#menu ul#tables a.active[href*="&select="] {border-left:2px solid #00f; margin-left:-4px; padding-left:18px;}
|
||||||
|
|
||||||
|
.links a[href*="&create="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJ6SURBVDjLpZNZSNRRGMV//2XGsjFrMg2z0so2K21xIFpepYUiAsGIICLffI8eWiBBeg3qQV+KwBYKLB8qpHUmrahcKLc0QsxldNSxdPz/79LD1ChBUXTh8sG93POdc75zDa01/7NsgGvPR09rzQmpVZZSCqlAKIWUCqk0QqoZWyKFRir1uvxIbsAGUFqXHQqkpP1L57M3Pm5MMJBKpQHUdF9BKIGQAlcJXOlOVykSdye3leO6MmkGQNyHw+uO/1X3bzGBK+S0B1IqAKqDg3986HeCZPffwvJtoNT7lOZLvUdtAPEDAKBkRzo3QwMUb89InN1uGGD3spdE214xe8MRUnM2MfppNW0Pqy7YAK5UKK2xLbhdP4hlmdxpGMQwwQT8ziNiI534c7cT6WrFazikzF2Eb8HS1IQEDdiWwcHAQmpehTkQSAcgNvSMiYFW5uUUMdV3HW+ywefGNqITJsbUUL75k4FWYJtQ+yaMZcXrk1ANk/33mbdiD7EvlRieETy+FJLkMFcjRRSW3emIAwiF1hqPBfu2LGSWbbA1uZ41SfWkrtxPrPcypsfFiWYzFGzGKTjFV28WEJeIUHETLdOgrmkI1VdHpCdEet5enP4qLK9mKrqMgedv6cyrAP+qxOTiUxAi7oEJi8frELoFoTLpa7nI/HQvscgSRt+0kV1SSW7qYtp7xrBMphm4Mi5h/VIfTcEq1u0oJaknSEdNiMYHET7UvcMpPEN31Ed7zxgASmk1I0g6dK66s8CRak5mVxjnfS05+TsZCw/T9baTx1nnGb47DrQksjE6HrsHYPz6nYt3+Sc3L8+wA2tz0J6pF5OD4WP7Kpq7f5fO79DfSxjdtCtDAAAAAElFTkSuQmCC") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
.links a[href$="&create="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIpSURBVDjLpZNPSFRRFMZ/749/Kt3IqFTSRoSMmrGIYTTbpEJtjBCCok1Em9JVG1dRC8FFEES5aGFEgRRZWq1iLKKxBiNqLDcltQgmHR9hY6LOu+feFm+YGVsZXbh8nHO53/nud8+xjDH8z3IB7r5avGgMZ8XoBq01okFpjYhGtEGJLtmCKINo/XbgVFPUBdDG9PVEq0P/UvnSvdlwQYFoHQIY/3obpRVKFL5W+OIXUVThrL91AN+XihKCwIeTu85sqPryqsJXUvRARAMwkshsiKB7fw25UgKVJwA40V7H/cl5jh+oL+RGk/P0xIqxl11dr8AXjTYG14HRNxkcx+ZhMoNlg52/ND6VAWMoc6F5+2Zy/l9PMIDrWByL1jI+tcDRaN06BaXxbDqLUnq9AqPBteHpuwUcJ0AIcgBXH93h+/wEyyuLrPk5cmv7gNY8gdIYYyhz4PDeWuIpj85IsS2ujQ2zJAk6DkZpqGnixcwYyU+PifUOX7Eh6DoAx7aIpzwA4imPeMrj+bTH+88PaNkZQWwhsrULsXxie9oAzgcESgUe2NAZCeE6AXZGQhwKh/Cyc5RZVXQ39wFwoeMmjXVhgMqiB8awe0cVP36u0Fi/iW9zvwuzkF3+xUz6Nal0gv6uWww+O02lUwGwmv8FM3l55EtLTvQWXwm+EkRpfNEoUZRXHCE5PUFbuJ0nH4cot1wSH14C3LA2Os6x3m2DwDmgGlgChpLX0/1/AIu8MA7WsWBMAAAAAElFTkSuQmCC") no-repeat scroll left bottom; padding-left:22px;}
|
||||||
|
|
||||||
|
#content p a[href*="&create="] {padding-left:22px;}
|
||||||
|
#content p a[href*="&select="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHISURBVDjLpVPNK0RRFP+9D98syMwUspHkm9I0YkFZWBFKkZ0s7a3Ewh+ilChK7FgoZCJFKYlYKB8zk2+Z5t0P577He29kQU7dd+6575zf+d1zztWklPiPmOozt/U4SThjXIoyIQS4AJjSXO0lGGlvcXAm6Vzsz4xUhm0AIeX4QLig+C+ZpxbOG1wGhGYHr1zMUmZGWRgs0ha3PE1nX/8mWmdgWTzLB+DUYbhm9FfZ35IEyrhXA3VXJfPbsV8B9LQUIeUHYJ8ASobag1jcucNgW8g9W4reYSDi2YnnZDoDiwCokDANct6NwTB0LEdj0HRA/wxa2SN25JNBEdWluUhZ366gqmAaGvrCAXKOozccTGPgt8+vn8GYSGcgyTYp3dpBnBg42nbQPRBTo5bTvqYkmxL6AQhNTWQGBXY3B7BxlEBXozcW64dxRKoKUZBju+P06gl5WaaviMJBM3TNDlbypemIZgHYOnlwASsCmW7nHADGnBoQ3c76YmweJ9BR5zFYjsbRHwm4tmJg6PhWA7pCXXk+bu7fURHKweXtq/sWaksz7SC/CCGFrwtyZ3r+rCnFRZ7qr1qc6mLZj4f9OEyPL8lVpbX/PucPv5QPKHB1TdEAAAAASUVORK5CYII=") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
#content p a[href*="&page="] {background-image:none; padding-left:0;}
|
||||||
|
#content p a[href$="&database="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIkSURBVDjLpVNNiFJRFP7eU1E0KSLTMpAwYSxyaidDtChm0WYQ3NSutv2s2kwwm2igNgMtooUQEQhhA9GqhSDTQsZZFDbNDBgVg5bSw9J8rzFF33udc+HGg0ladOHj3nPe+b7zc99VbNvG/yy30yiVSl4SnCNcsixrivYEgY7WJu0faX9EKGUyGVNyFFkBkY/T+WkoFEpFIhEEAgH4/X7w916vB8Mw0Gg00G63y+S7mM1mm4LIAYxisbhSr9c5nT1pjUYju1qt2oVC4YnkqbIUMk6Ew+F/9hyNRkFJLuyaATmFoqZp8Pl88Hq98Hg8wtfv99HpdNBsNhGPx0XsRAG3241ut4vBYCDs8XgMXdcxHA7FN/b9VUD25HK5RAUczKC+hYgcNpNN05xcAQdLkqIoIlj6VFWdXIEUkAQGV8M2k2vaG3z6sYGfVR39XzsHlm/dX3h5d31xlwAHM5goBd5+LuO75z3OnU3jyP4EVrZeKGub2p309cP7VKcAQ2Znoiz3deMVTk1Nw1RNTB+ahamMkD45w7RrfwSYwFdFf6K4Quf6pmvwKHswl7wh7Jvnc4gfTPHR52zhcqVSeZZMJgOxWEyI8BC5CmOnh63WKtZbZczPPsa94hX4XCLJQHG+xnw+f5SEFghZmvhefgvcTqn2HN3gBmZSZ5CInMaHr1Wsvivjy3ZvSZn0nHO5XJDIxwgWDbW2vL10m9xXCUGCQXi49qA1/xvyq6BCh7yZeQAAAABJRU5ErkJggg==") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
#content p a[href*="&edit="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLpZPrS5NhGIf9W7YvBYOkhlkoqCklWChv2WyKik7blnNris72bi6dus0DLZ0TDxW1odtopDs4D8MDZuLU0kXq61CijSIIasOvv94VTUfLiB74fXngup7nvrnvJABJ/5PfLnTTdcwOj4RsdYmo5glBWP6iOtzwvIKSWstI0Wgx80SBblpKtE9KQs/We7EaWoT/8wbWP61gMmCH0lMDvokT4j25TiQU/ITFkek9Ow6+7WH2gwsmahCPdwyw75uw9HEO2gUZSkfyI9zBPCJOoJ2SMmg46N61YO/rNoa39Xi41oFuXysMfh36/Fp0b7bAfWAH6RGi0HglWNCbzYgJaFjRv6zGuy+b9It96N3SQvNKiV9HvSaDfFEIxXItnPs23BzJQd6DDEVM0OKsoVwBG/1VMzpXVWhbkUM2K4oJBDYuGmbKIJ0qxsAbHfRLzbjcnUbFBIpx/qH3vQv9b3U03IQ/HfFkERTzfFj8w8jSpR7GBE123uFEYAzaDRIqX/2JAtJbDat/COkd7CNBva2cMvq0MGxp0PRSCPF8BXjWG3FgNHc9XPT71Ojy3sMFdfJRCeKxEsVtKwFHwALZfCUk3tIfNR8XiJwc1LmL4dg141JPKtj3WUdNFJqLGFVPC4OkR4BxajTWsChY64wmCnMxsWPCHcutKBxMVp5mxA1S+aMComToaqTRUQknLTH62kHOVEE+VQnjahscNCy0cMBWsSI0TCQcZc5ALkEYckL5A5noWSBhfm2AecMAjbcRWV0pUTh0HE64TNf0mczcnnQyu/MilaFJCae1nw2fbz1DnVOxyGTlKeZft/Ff8x1BRssfACjTwQAAAABJRU5ErkJggg==") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
#content p a[href*="&table="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJcSURBVDjLpZPtT5JhFMafrW997I9rscA+FFu2QRurtlw5cQ4InLpwBogIPNFSiNJ4C+JVkj0QTBHQKFPQlJfwlanY1tXz3ARkn2jd27Wz++yc33XOvd0UAOp/RNGR/X5zeH9rOlTDVKAK3fsqJrxlqN27GHPuYHh+G4rXRQzZNjEws47Hli/oo/PxNsAU3qvWT3/gX3TPuHrWBhiC30nSktXDtKLB1NI4NKkxqBMqjDByPFkcxNBCPwbCfXgUeEBq705m0AZM+qsk2e3hau88W+4ANOy+XPLFQrkrcbW31KkOYJx9rBaAOzPR0gVHW6x593q9cDgcqB6e4sZoogMYdXzD0ck5ZhfLsHGKVfAqVoadKcMdzcLr82PuwwZCoRACgQCWVzdhoK2gaVpDAMNzWzhkAXamQpze/I4t13w+j2AwiFwuh7W1NXg8HmQyGSgUCshkssuU3F7AQf0c84kK3n68KFc4hXQ6DavVCqlUCqVSSdaIx+NQq9UGMsHg7Ab2jxtwp5rOvqUqia3CUqnEObWn0mp1KBaLcLlckMvloPpfrhOAl230/SGLxQK3241CoQC9Xg9nskKk1emQzWZZkBZCoRBU3/NP2GMBgXTTObjSjI1GA8lkEgzDwO/3E4iObXY6nYhEIhCJRHoWcIW6b1pF7egMlYNT7NROUKzU8XX3GJ+3D2E0GgmAm4Zbh2s0mUyIRqMcAGKx+BIlMeSiYu1K/fbEMm4+TaFnJIHrSgZX5TFIZNPo7e1Fj9QOs9kMlUqFaw9pCASCnzwe7x15xG6/rUQiAZ/Px9/5XyhZOMVGKlOdAAAAAElFTkSuQmCC") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
th a[href*="&table="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHISURBVDjLpVPNK0RRFP+9D98syMwUspHkm9I0YkFZWBFKkZ0s7a3Ewh+ilChK7FgoZCJFKYlYKB8zk2+Z5t0P577He29kQU7dd+6575zf+d1zztWklPiPmOozt/U4SThjXIoyIQS4AJjSXO0lGGlvcXAm6Vzsz4xUhm0AIeX4QLig+C+ZpxbOG1wGhGYHr1zMUmZGWRgs0ha3PE1nX/8mWmdgWTzLB+DUYbhm9FfZ35IEyrhXA3VXJfPbsV8B9LQUIeUHYJ8ASobag1jcucNgW8g9W4reYSDi2YnnZDoDiwCokDANct6NwTB0LEdj0HRA/wxa2SN25JNBEdWluUhZ366gqmAaGvrCAXKOozccTGPgt8+vn8GYSGcgyTYp3dpBnBg42nbQPRBTo5bTvqYkmxL6AQhNTWQGBXY3B7BxlEBXozcW64dxRKoKUZBju+P06gl5WaaviMJBM3TNDlbypemIZgHYOnlwASsCmW7nHADGnBoQ3c76YmweJ9BR5zFYjsbRHwm4tmJg6PhWA7pCXXk+bu7fURHKweXtq/sWaksz7SC/CCGFrwtyZ3r+rCnFRZ7qr1qc6mLZj4f9OEyPL8lVpbX/PucPv5QPKHB1TdEAAAAASUVORK5CYII=") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
|
||||||
|
.links #content a[href*="&database="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKRSURBVDjLhVNLTBNRFD3TTju1FCcBaxuaQEr94ydiZIHGpcFISBOi0YREZWHCVoyRxKUxxq0LXcACQyLsjO6KjSEiDfHDz0S0CLSxlFKd2g7MTDudGd+bMAQCxJucuXfuu+fcO/PeYwzDALVoNMqRuI3guq7rR4g/SEBC/Svxc8T3EUTD4bCGTcZQAUI+RvxLr9d70u/3o6KiAm63G3Qtn89DFEUkk0lks9lRkrvW3t6e2lCgRZFI5F0ikaDtjN1MVVVjYmLCGBoa6qccC7Z1kQafz4f/WSAQAGlyaXOOpQ+SNNUymQxcLhc4joPD4TBzkiRBEASkUimEQiGzdlcBlmWRy+WgKIr5Xi6XUSgUUCwWzTVN+IAzeOOde71orP0eAaOkbrDWf6Cw2+3mBLSYgny3KULXPOUY2BUB/hMd4IOn8XfhMGYjvU+2TECLLRLDMNA0zYw5JYa6Ghke/hyEn9/gZEqo3OuHp7qW3yJgESjoNPSdlb8gWCOCr29BMT0Ip5tBYnIWqlL6o8irzVsEaHcKSqQCen4cweok+FAblNRz2JxlODx1cEkzGWmVbTl7Z/jHhgCF1Z3GYjIKf+U8+ANhQn4Gm6OMUiGI9MhHg5Gl1sbu8UnKNc8B7Ui3ipxEcwvlpVFw6hz2N1xGabkXdqeBYqEOmfefEZWac4e6xz9Z22hbn+BmLBZbi8fjEBdG4NF/QdUDSM88hQ4FawKJR6cxLDZl86qzZdtdoDYwMBAkQg/2LL/ovNLVh++Dd7G0OAau9hTkrKgnnE39GW3f/Z6enpUdBSx7ePu4eq+zi4VNw+TbV0gsxFd5b9X5i4+mpnY63tsErl6okhvrfWzT0SAMR3FMXsnean08Pb/b/fgHqpjCspi90kkAAAAASUVORK5CYII=") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
#content p a[href*="&schema="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFOSURBVDjLtVK7SgNRED0b9iuM2lr4QK1DQIyk0FZsJAj+gH+ilRZb2NjaRHTLmA9QFKz9huzm7t37Hu+u7IJgQjR6YLjDzOXMmcMERIR5EE5qXA4z4sqACYWEC5wfLQXf/WtMIuDSoL0A7DZDjBj/uYI0l8jzEEJYJMkvCEZM4PqZIxlzpGk+kSCY18TGtGYcx9Tv96dOqBUMBgNyzsFaC621312Ac+59yJFlGRhj5VvVoigKvniglEK32w1mkd3r9ejPPAjOhqdknYX18p1/rzo3pYqTh0OSRkJI5UMgPn4s61sX66SkhtEGcISGsQad5gH2FvehfV5BaIF2cwet5RZyKeu68pe5ubKG7dUNP5AQGltMN57Mosgr5EIiVQmYGvtc1PVicqHY+dXpk8Dg7v22XKFo1ARe9v1bDOlXKKKCs4Sn1xdU1v3vIc2CD3bN4xJjfJWvAAAAAElFTkSuQmCC") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
#content p a[href*="&privileges="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGnSURBVDjLxVM9T8JQFD2lBRQRFPAjEhIwXRgwEUjERRMnZPYXuPoHSJz8BQ7+BCcHJzZHiST4MWC0fsQBJUKCEQnGWihtfa/QBgK6MHiT2/tu3+1559x3y2iahlHMghFtZACuUqmkSdyiiaqqoJJo7HVFUQZi1w+Zcrnc9vl8LAXo7YexHhaNdaFQUDiSsFarFftXClriJz4eTqHJX7DMhDEbiiDlfALP80if1CFKLYjfxCUZR9s8ZcByvadG7w/QLl/guVRC8U2Etnutv5dlGXvr44S+3ZRFjcrgjEQvnFvG2pIfGzYOZ5c3KLCsWZjNZvual0wmOwD0oX/cVnFeXQDHfcDpcuGqtYJaTQKcHYBEImGebrDuY1AvPWImtIhjLoraSwtjHhvmv17Nwnw+/zcD/0QDt+9TUD1TmJ6zQWpImFTfyM64XhiLxdAr17h2E2Bnc9XcEEURzWaTFARM2na7HW63+3eAEul8tVqF1+tFMBiEw+EYmDpBEFAsFhEIBBCJRHQAhgyDQEDCFCgejzO5XE4bNn2Gp1IpJpPJaF05d8y//40/Nlww9OOVWvkAAAAASUVORK5CYII=") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
|
||||||
|
#content p a[href*="&event="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJlSURBVDjLpZNbaM9hGMc/v8NsTqF2yMhZKIexmpFyRZIiblwouZDauLYoTVEryg2uXIoIOUU5zJkh4e9QDmtJbWxrbUzz/72/9/m6+P3HLslbT9/3ufm+n/f7Pm8gif9ZMcDxe717JLZ62UQzwxukZnhveBOptyHl8anwZk/3b5pZEwOYtGNDzejSfzm58dTH+b8JvFkpwMizdSCBT8E8OJftkzy4BPIOnONHQzPO+eIhBoM5CCrLwNKslBZM8uDykCbwtgMAl/o/GXhvBYMA2rtAlpGYZSR+UIGKCgCSggGSOHy1Q/0DTifufZUknbr/RZJ0+mHWn3mU9edbMu3qG9DmQ08lKSNw3jCJOIKzjzqJopBzLZ3EEVx40smDr/u4e96QGUXPGpkzYQSJywjCwSsIiKOADUvKiUNYX1tOUQhra8oJg4hZ02cQhhGrqyuyp03tTwbOGzKIQ7j8rIsn3Qd4fEVIIn6+kzAMaH35Fn37wbZD68gnCUl+EbAkI3CpIYmiCNZUlwEwbfIUgiBg1cIyJqbzGFPiWbl8GXUb66mqnkrJ2IvUbq88GEI2dQBRGHDjZTcAbZ8+ERDQnOvm+fszVM1egA89C8avwAeO2nlLAeqRxK7j79TzPa/mXJck6darTG8XdM3uhbry+piGrou5I1pcP17h7wwk5k4aRUfPANMrhtP2pZ8J44bx7nMfff29vGl/SNP1LQA0XdtCa2cO4GdhkPRg78kPVYm3kS71uNTjU8N5I/UpxSWracndZOn8ZVx6dZRhQcz9F3cAjgR/+51rt1c2AXXAaOA7cLTlcHvDL6y6kIpO9lqsAAAAAElFTkSuQmCC") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
#content p a[href$="&view="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gEJFwohC2f85AAAAsxJREFUOMulk8tr3HUAxD+/1z5+u5smMYlmm6R1rTlEmrY0RKqtWMVWW6qtePBv8NaKUFGUgmBB9CJ48aIH8SKGBhGKKEIaPdgam0gTQ7Bm3U2zTbKPZrP7+32fHkqDx0LnNocZmGHGsdbyIHB5QPj/J8Vi8bDV8m0RtY5qGaeNMVijsdbiOF6cDDNXw47u8/l8/so9jXMvQqlU+lK0m6+LdjMRBAFCCBq1DYRU+L5P944OtIiRxtru/qHvhh7d8/K2QalUutysrx/zHEu5XGZl9TbWDUilsygDSimMbJGyEYVdAwB09g3MPTayb9RfXl7+RLTuHHOtYn5hkYHCCH5nAW0sUmmEVCiliIRisL8HWf8b0apj4qW9CvcL5+bSX6uNjcrD2gkYPTDG7zf+YWsrRinF9I8TWGt5Yvw4QioSgc+rx8e5uThPeWGGRrOt/Np6pa83v4ue3j4AlFBEsUDru+UBxEIihdwue7DwOKl0yPTlS77f0z9kMtmcp7XGdV2ymQRr1Qax1AwfeA6pNHEUEwvNYL4XYwxaa7Kd3QzsLlhnfm6mms11dOU6H8L3fep3WswuFFmrNu7ml4pYaKJwllv2Gk1RRciY8f7nGW7tEd7Zc28WNjcqB0W0heMFpNNJUgkPqRSbm1toY2lnZmj2/cYzB5/khf2naGf+ZerWNyyVVyqOtZbZ63/8EtVWDyV9j0xXby0Z5qJkmBGu5xuAcxOndr924kXHeA6nH3mPj+bO4OPy1beTkQ8wum//U9NXpi7drpRPhBtrXWEYkkikcIMEAGvNFQLTwUs7zwLw1t4JJosXgcnU9pSfPnzkla8//zRZbtRPR7E447ru0cB1soBTjdbTf1Z/4lrtB94d/Z4Pr58k5aYAIud+3jh2Ifhg53D4zpGRZxnuOsRi7VembvxMebH1sXO/dx67EFwE3gBywCbw2dX35fn/ANYEZmpcDQc/AAAAAElFTkSuQmCC") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
|
||||||
|
#content p a[href*="&refresh="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAI/SURBVDjLjZPbS9NhHMYH+zNidtCSQrqwQtY5y2QtT2QGrTZf13TkoYFlzsWa/tzcoR3cSc2xYUlGJfzAaIRltY0N12H5I+jaOxG8De+evhtdOP1hu3hv3sPzPO/z4SsBIPnfuvG8cbBlWiEVO5OUItA0VS8oxi9EdhXo+6yV3V3UGHRvVXHNfNv6zRfNuBZVoiFcB/3LdnQ8U+Gk+bhPVKB3qUOuf6/muaQR/qwDkZ9BRFdCmMr5EPz6BN7lMYylLGgNNaKqt3K0SKDnQ7us690t3rNsxeyvaUz+8OJpzo/QNzd8WTtcaQ7WlBmPvxhx1V2Pg7oDziIBimwwf3qAGWESkVwQ7owNujk1ztvk+cg4NnAUTT4FrrjqUKHdF9jxBfXr1rgjaSk4OlMcLrnOrJ7latxbL1V2lgvlbG9MtMTrMw1r1PImtfyn1n5q47TlBLf90n5NmalMtUdKZoyQMkLKlIGLjMyYhFpmlz3nGEVmFJlRZNaf7pIaEndM24XIjCOzjX9mm2S2JsqdkMYIqbB1j5C6yWzVk7YRFTsGFu7l+4nveExIA9aMCcOJh6DIoMigyOh+o4UryRWQOtIjaJtoziM1FD0mpE4uZcTc72gBaUyYKEI6khgqINXO3saR7kM8IZUVCRDS0Ucf+xFbCReQhr97MZ51wpWxYnhpCD3zOrT4lTisr+AJqVx0Fiiyr4/vhP4VyyMFIUWNqRrV96vWKXKckBoIqWzXYcoPDrUslDJoopuEVEpIB0sR+AuErIiZ6OqMKAAAAABJRU5ErkJggg==") no-repeat scroll 2px bottom; padding-left:22px;}
|
||||||
|
|
||||||
|
#content p a[href*="&sql="] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJ5SURBVHjapFNLaxNRFP4mnZmQZtKYpJ2I8ZE0NSbSWKpgBYNUhIJQUDddCaILwb34C0RcCi60vpa14sZFoSjxUWxTFEubFkxf9kFS+7Jp0yQmM5mH905SF9pSwQt3vnPnnvPNd86cA13Xsds+23XKsdMdQx//s0z/6hi54he3e8/0d3ZuK0FTFGhUJkVVhSbLUAiq5IxSCQrBEkGWOu8Lh38HGrlpWnlXCKhNSbZQo3cEE9FomYAuKZstE2wFa8RR1cpKqK2o4JhxmLVJVAlh5Df6IFj5MgENcHq9EAMBg2RucBDVLpdxVon0iXdvIf8sQHRIsNReht3XjI3ZI8iuPAOrkDyorBq3G6vT08ikUuAsFiN46HkXXL56eI41QV1/BMF+GulvCfCMDFvNXtSIHrC0KDTPhXgc7lAQdQ2thk2XIpewnkzCJvTA4T8PabEbfDWD+ZFxyEUdX94sw6QVCkZlxWAQU+8/YGZgALV+v0HgbWmBNzQMR0M7it8fg+HWwQk2cLkkcvJh5NNFsCVJMgq1ubiE5o4OI5CS8FYrya8b9saLKC48gIlTIG/6sBaLw3PpHrTEMrSXr4kPIaAKFkbjSA0PEVuFlB+DO8Ah2HoB8tITVPE6iplDWOn7jLn0CSQevoDV6TI+zEpUASkkZzaTYrLIrH3C/qMCFEbE4th9OEQexfQBrPYP44d+DuY91eBp+1dGgFVIDb729kKpdJfQtIy2yFNMdN/E5McYzAebUEhOYjZuQXaj5+9W/nOYbl9vLN26doOFScVI9BXmZ6dy9jpnpO1O5dfsRtBxxlk4Xu9mT4Z80DkpVlhZvdp+d3RmpyH7JcAAnHiAVYWMsdkAAAAASUVORK5CYII=") no-repeat scroll 2px bottom; padding-left:24px;}
|
||||||
|
|
||||||
|
table tbody input[name*="check"] {display:block; float:left;}
|
||||||
|
|
||||||
|
table a[href*="&edit="][href*="&where"] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFUSURBVDjLrZM/SAJxGIZdWwuDlnCplkAEm1zkaIiGFFpyMIwGK5KGoK2lphDKkMDg3LLUSIJsSKhIi+684CokOtTiMizCGuzEU5K3vOEgKvtBDe/2Pc8H3x8NAM1fQlx4H9M3pcOWp6TXWmM8A7j0629v1nraiAVC0IrrwATKIgs5xyG5QiE+Z4iQdoeU2oAsnqCSO1NSTu+D9VhqRLD8nIB8F0Q2MgmJDyipCzjvYJkIfpN2UBLG8MpP4dxvQ3ZzGuyyBQ2H+AnOOCBd9aL6soh81A5hyYSGWyCFvxUcerqI4S+CvYVOFPMHxLAq8I3qdHVY5LbBhJzEsCrwutpRFBlUHy6wO2tEYtWAzLELPN2P03kjfj3luqDycV2F8AgefWbEnVqEHa2IznSD6BdsVDNStB0lfh0FPoQjdx8RrAqGzC0YprSgxzsUMOY2bf37N/6Ud1Vc9yYcH50CAAAAAElFTkSuQmCC") no-repeat scroll right bottom; padding-right:18px;}
|
||||||
|
|
||||||
|
table input + a[href*="&edit="][href*="&where"] {width:0; float:left; display:block; height:16px; overflow:hidden; text-decoration:none; padding:0 0 0 18px; background-position:2px bottom;}
|
||||||
|
|
||||||
|
table thead a[href*="&modify=1"] {background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gEKDjA70u7s9gAAAMZJREFUOMvtksFpwzAUhr9fcUsWyAQewWDIHt6iLrlriBDIBl4maARPYNH6VtAh2FEuCSQksUV77Qc66Ef63nvw5JyLTNA0DXme03UdVVU9PnDOxWEYJk9d109z733MrqLN/oAkiBEJEAjYfqwJIbzsMLu9vC9XoIgxJ94yEX6+meNOoAXACWPAmEsnqQIBx/CFgEGRo0DEdMHuc81vkPd+soy1lqIoaNsWa+2jYG4PkkYoy/IuHMcx6XPf9xj+yL8gYQ/mOAPpeHRKiIdANAAAAABJRU5ErkJggg==") no-repeat scroll right bottom; }
|
||||||
|
table thead a[href*="&modify=1"] {width:0; display:inline-block; height:16px; overflow:hidden; text-decoration:none; padding:0 0 0 16px; vertical-align: middle;}
|
||||||
|
table td:first-child {white-space:nowrap;}
|
||||||
|
|
||||||
|
input[name="delete"], input[name="drop"] {background:transparent url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHSSURBVHjapFM5bsJQEB2zSIDFJrHYpEtyAyoKJAp6CrqIkBPkNDlBAKXjBEgUpKOBCyQNijFiEZvZl8z7wsjESYpkpNFfPO/Nmz9j6Xg80n/M9fWi3W7fMOnd4XAo8qogAbvO5xKvL6lU6s0aL1kVMDjP5ye/36+Gw2FyOp3EQFqtVtTr9WixWHT5/JhOp6s2ghP4ORaLyaFQiGazGa3Xa0HgdrvJ6/WSpmk0Go0MjnvIZDLVM0Gr1brm/WskEkkA3O/3abvdQjq5XC6xgoiVka7rNB6PNT6ns9nsu+OkpODxeBLBYJAGgwHt9/uzQ8Vms6Hdbie+KYqC+ASTFrARBMx2HwgEaDKZiHqn0yktl0uxtzrMMAyKx+MCc+4Cs13hwQCC1GQy+W3Lms2mUIUygbEqEBLNun8z8zswVgUfLO0WD4Z6kekn8/l8okNM8GFVUMYDoVWQ6HA4bEAzoyzL1O12kbRsJajwhYZhiUajJEnShWSAQaqqKnU6HahEGysXg9RoNPJ8+cwZZLSKp47m8/k5Kxzg4XBocNxDLper2ka5Xq+LUeatilahJLN1mEJ+ZDHKJthGAKvVauJnYi9ysHIqQee1xOsLg3/+mf5inwIMAJMhb74NwG5wAAAAAElFTkSuQmCC") no-repeat scroll left center; padding:1px 5px 1px 18px; border:0; cursor:pointer; font-size:.9em;}
|
||||||
|
input[name="delete"]:hover, input[name="drop"]:hover {color:red; background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJdSURBVDjLpZP7S1NhGMf9W7YfogSJboSEUVCY8zJ31trcps6zTI9bLGJpjp1hmkGNxVz4Q6ildtXKXzJNbJRaRmrXoeWx8tJOTWptnrNryre5YCYuI3rh+8vL+/m8PA/PkwIg5X+y5mJWrxfOUBXm91QZM6UluUmthntHqplxUml2lciF6wrmdHriI0Wx3xw2hAediLwZRWRkCPzdDswaSvGqkGCfq8VEUsEyPF1O8Qu3O7A09RbRvjuIttsRbT6HHzebsDjcB4/JgFFlNv9MnkmsEszodIIY7Oaut2OJcSF68Qx8dgv8tmqEL1gQaaARtp5A+N4NzB0lMXxon/uxbI8gIYjB9HytGYuusfiPIQcN71kjgnW6VeFOkgh3XcHLvAwMSDPohOADdYQJdF1FtLMZPmslvhZJk2ahkgRvq4HHUoWHRDqTEDDl2mDkfheiDgt8pw340/EocuClCuFvboQzb0cwIZgki4KhzlaE6w0InipbVzBfqoK/qRH94i0rgokSFeO11iBkp8EdV8cfJo0yD75aE2ZNRvSJ0lZKcBXLaUYmQrCzDT6tDN5SyRqYlWeDLZAg0H4JQ+Jt6M3atNLE10VSwQsN4Z6r0CBwqzXesHmV+BeoyAUri8EyMfi2FowXS5dhd7doo2DVII0V5BAjigP89GEVAtda8b2ehodU4rNaAW+dGfzlFkyo89GTlcrHYCLpKD+V7yeeHNzLjkp24Uu1Ed6G8/F8qjqGRzlbl2H2dzjpMg1KdwsHxOlmJ7GTeZC/nesXbeZ6c9OYnuxUc3fmBuFft/Ff8xMd0s65SXIb/gAAAABJRU5ErkJggg==")}
|
||||||
|
|
||||||
|
input[name="logout"] {background:transparent url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJHSURBVDjLlZPNi81hFMc/z7137p1mTCFvNZfGSzLIWNjZKRvFRoqNhRCSYm8xS3+AxRRZ2JAFJWJHSQqTQkbEzYwIM+6Yid/znJfH4prLXShOnb6r8/nWOd8Tcs78bz0/f+KMu50y05nK/wy+uHDylbutqS5extvGcxaWqtoGDA8PZ3dnrs2srQc2Zko41UXLmLdyDW5OfvsUkUgbYGbU63UAQggdmvMzFmzZCgTi7CQmkZwdEaX0JwDgTnGbTCaE0G4zw80omhPI92lcEtkNkdgJCCHwJX7mZvNaB0A14SaYJlwTrpHsTkoFlV1nt2c3x5YYo1/vM9A/gKpxdfwyu/v3teCayKq4JEwT5EB2R6WgYmrs2bYbcUNNUVfEhIfFYy69uci+1fuRX84mkawFSxd/4nVWUopUVIykwlQxRTJBTIDA4Pp1jBZPuNW4wUAPmCqWIn29X1k4f5Ku8g9mpKCkakRLVEs1auVuauVuyqHMo8ejNCe+sWPVTkQKXCMmkeZUmUZjETF1tc6ooly+fgUVw9So1/tRN6YnZji46QghBFKKuAouERNhMlbAHZFE6e7pB+He8MMw+GGI4xtOMf1+lsl3TQ4NHf19BSlaO1DB9BfMHdX0O0iqSgiBbJkjm491hClJbA1LxCURgpPzXwAHhg63necAIi3XngXLcRU0fof8ETMljIyM5LGxMcbHxzvy/6fuXdWgt6+PWncv1e4euqo1ZmabvHs5+jn8yzufO7hiiZmuNpNBM13rbvVSpbrXJE7/BMkHtU9jFIC/AAAAAElFTkSuQmCC") no-repeat scroll right center; padding:1px 22px 1px 5px; border:0; cursor:pointer; font-size:.9em;}
|
||||||
|
input[name="logout"]:hover {color:red;}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
adminer:
|
||||||
|
image: adminer:latest
|
||||||
|
container_name: adminernexus
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "5006:8080"
|
||||||
|
# - 127.0.0.1:8080:8080
|
||||||
|
environment:
|
||||||
|
ADMINER_PASSWORD: "p4ssw0rd"
|
||||||
|
volumes:
|
||||||
|
- ../database:/db
|
||||||
|
# - ./db:/db
|
||||||
|
- ./plugins-enabled:/var/www/html/plugins-enabled
|
||||||
|
- ./adminer.css:/var/www/html/adminer.css
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
require_once('plugins/login-password-less.php');
|
||||||
|
|
||||||
|
return new AdminerLoginPasswordLess(password_hash($_ENV["ADMINER_PASSWORD"], PASSWORD_DEFAULT));
|
||||||
@@ -1,24 +1,34 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
|
import os
|
||||||
import io
|
import io
|
||||||
from flask import Flask, render_template, session, request, redirect, url_for, g, flash, send_file, jsonify
|
from flask import Flask, render_template, session, request, redirect, url_for, g, flash, send_file, jsonify
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
from werkzeug.exceptions import RequestEntityTooLarge
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.secret_key = 'clave-secreta-navbar-app'
|
app.secret_key = 'clave-secreta-navbar-app'
|
||||||
|
# Límite máximo de subida: 5 MiB
|
||||||
|
# app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024
|
||||||
|
|
||||||
DATABASE = 'database/pyfleet.db'
|
DATABASE = 'database/pyfleet.db'
|
||||||
|
|
||||||
PASSWORD_HASH = generate_password_hash('123')
|
PASSWORD_HASH = generate_password_hash('123')
|
||||||
|
|
||||||
|
|
||||||
# Duración de inactividad máxima
|
# Duración de inactividad máxima
|
||||||
SESSION_TIMEOUT = timedelta(hours=4)
|
SESSION_TIMEOUT = timedelta(hours=4)
|
||||||
|
|
||||||
|
UPLOAD_FOLDER = 'uploads/documentos'
|
||||||
|
ALLOWED_EXTENSIONS = {'pdf'}
|
||||||
|
|
||||||
|
|
||||||
# ── Auth ─────────────────────────────────────────────────────
|
# ── Auth ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
def login_required(f):
|
def login_required(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated(*args, **kwargs):
|
def decorated(*args, **kwargs):
|
||||||
@@ -66,10 +76,30 @@ def logout():
|
|||||||
session.clear()
|
session.clear()
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
|
||||||
|
# ── Funcion normalizar ───────────────────────────────────────────
|
||||||
|
|
||||||
|
def normalizar(texto):
|
||||||
|
# if texto is None:
|
||||||
|
# return ''
|
||||||
|
texto = str(texto).strip().lower()
|
||||||
|
|
||||||
|
# Quitar tildes
|
||||||
|
texto = unicodedata.normalize('NFD', texto)
|
||||||
|
texto = ''.join(
|
||||||
|
c for c in texto
|
||||||
|
if unicodedata.category(c) != 'Mn'
|
||||||
|
)
|
||||||
|
|
||||||
|
texto = ' '.join(texto.split()) # Convertir varios espacios seguidos en uno
|
||||||
|
|
||||||
|
return texto
|
||||||
|
|
||||||
# ── DB helpers ────────────────────────────────────────────────
|
# ── DB helpers ────────────────────────────────────────────────
|
||||||
def get_db():
|
def get_db():
|
||||||
if 'db' not in g:
|
if 'db' not in g:
|
||||||
g.db = sqlite3.connect(DATABASE)
|
g.db = sqlite3.connect(DATABASE)
|
||||||
|
g.db.create_function("NORM", 1, normalizar)
|
||||||
g.db.row_factory = sqlite3.Row
|
g.db.row_factory = sqlite3.Row
|
||||||
return g.db
|
return g.db
|
||||||
|
|
||||||
@@ -80,63 +110,24 @@ def close_db(e=None):
|
|||||||
|
|
||||||
def init_db():
|
def init_db():
|
||||||
db = sqlite3.connect(DATABASE)
|
db = sqlite3.connect(DATABASE)
|
||||||
|
db.create_function("NORM", 1, normalizar)
|
||||||
|
# db.commit()
|
||||||
|
# db.close()
|
||||||
|
|
||||||
# ... tablas existentes ...
|
# ... tablas existentes ...
|
||||||
db.execute('''
|
db.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS mensajes (
|
CREATE TABLE IF NOT EXISTS documentos (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
texto TEXT NOT NULL,
|
vehiculo_id INTEGER NOT NULL REFERENCES vehiculos(id) ON DELETE CASCADE,
|
||||||
autor TEXT,
|
nombre TEXT,
|
||||||
|
filename TEXT NOT NULL,
|
||||||
fecha DATETIME DEFAULT (datetime('now','localtime'))
|
fecha DATETIME DEFAULT (datetime('now','localtime'))
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
|
||||||
db.execute('''
|
|
||||||
CREATE TABLE IF NOT EXISTS vehiculos (
|
|
||||||
"id" INTEGER,
|
|
||||||
"m_oficial" TEXT,
|
|
||||||
"m_civil" TEXT,
|
|
||||||
"m_propia" TEXT,
|
|
||||||
"VIN" TEXT,
|
|
||||||
"poliza" TEXT,
|
|
||||||
"viat" TEXT,
|
|
||||||
"legajo" TEXT,
|
|
||||||
"marca" TEXT,
|
|
||||||
"modelo" TEXT,
|
|
||||||
"tipo" TEXT,
|
|
||||||
"denominacion" TEXT,
|
|
||||||
"color" TEXT,
|
|
||||||
"cilindrada" TEXT,
|
|
||||||
"potencia" TEXT,
|
|
||||||
"combustible" TEXT,
|
|
||||||
"neumaticos" TEXT,
|
|
||||||
"plazas" TEXT,
|
|
||||||
"situacion" TEXT,
|
|
||||||
"poblacion" TEXT,
|
|
||||||
"unidad" TEXT,
|
|
||||||
"grupo" TEXT,
|
|
||||||
"int_grupo" TEXT,
|
|
||||||
"int_juzgado" TEXT,
|
|
||||||
"int_procedimiento" INTEGER,
|
|
||||||
"f_destino_situacion" TEXT,
|
|
||||||
"f_alta" TEXT,
|
|
||||||
"f_baja" INTEGER,
|
|
||||||
"km" TEXT,
|
|
||||||
"km_1" TEXT,
|
|
||||||
"itv_ultima" TEXT,
|
|
||||||
"itv" TEXT,
|
|
||||||
"observaciones" TEXT,
|
|
||||||
"historial" TEXT,
|
|
||||||
"notas" TEXT,
|
|
||||||
PRIMARY KEY("id" AUTOINCREMENT)
|
|
||||||
)
|
|
||||||
|
|
||||||
''')
|
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
# ── Rutas generales ───────────────────────────────────────────
|
# ── Rutas generales ───────────────────────────────────────────
|
||||||
@app.route('/', methods=['GET', 'POST'])
|
@app.route('/', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@@ -217,43 +208,55 @@ def vehiculos_list():
|
|||||||
if 'incl_bajas' in request.args:
|
if 'incl_bajas' in request.args:
|
||||||
incl_bajas = request.args.get('incl_bajas') == '1'
|
incl_bajas = request.args.get('incl_bajas') == '1'
|
||||||
session['incl_bajas'] = incl_bajas
|
session['incl_bajas'] = incl_bajas
|
||||||
else:
|
else: # Si no viene en la URL, recuperamos el último estado
|
||||||
# Si no viene en la URL, recuperamos el último estado
|
|
||||||
incl_bajas = session.get('incl_bajas', False)
|
incl_bajas = session.get('incl_bajas', False)
|
||||||
|
|
||||||
|
|
||||||
terminos = [t.strip() for t in q.split(',') if t.strip()] if q else []
|
terminos = [t.strip() for t in q.split(',') if t.strip()] if q else []
|
||||||
|
|
||||||
# Filtro base de bajas
|
# Filtro base de bajas
|
||||||
filtro_bajas = '' if incl_bajas else "AND (situacion IS NULL OR UPPER(situacion) != 'BAJA')"
|
filtro_bajas = '' if incl_bajas else "AND (situacion IS NULL OR UPPER(situacion) != 'BAJA')"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# OPCION 1. BAJAS SOLO MUESTRA BAJAS
|
|
||||||
if terminos:
|
if terminos:
|
||||||
condiciones = []
|
condiciones = []
|
||||||
params = []
|
params = []
|
||||||
for t in terminos:
|
|
||||||
like = f'%{t}%'
|
|
||||||
condiciones.append('''(
|
|
||||||
m_oficial LIKE ? OR m_civil LIKE ? OR VIN LIKE ?
|
|
||||||
OR marca LIKE ? OR modelo LIKE ? OR poblacion LIKE ?
|
|
||||||
OR unidad LIKE ? OR grupo LIKE ? OR tipo LIKE ?
|
|
||||||
OR situacion LIKE ? OR int_operacion LIKE ?
|
|
||||||
)''')
|
|
||||||
|
|
||||||
params.extend([like] * 11)
|
for t in terminos:
|
||||||
|
t_norm = normalizar(t)
|
||||||
|
|
||||||
|
if t_norm == 'lanzadestellos':
|
||||||
|
condiciones.append('lanzadestellos = 1')
|
||||||
|
continue
|
||||||
|
|
||||||
|
like = f'%{t_norm}%'
|
||||||
|
condiciones.append('''(
|
||||||
|
NORM(m_oficial) LIKE ? OR
|
||||||
|
NORM(m_reservada) LIKE ? OR
|
||||||
|
NORM(VIN) LIKE ? OR
|
||||||
|
NORM(marca) LIKE ? OR
|
||||||
|
NORM(modelo) LIKE ? OR
|
||||||
|
NORM(poblacion) LIKE ? OR
|
||||||
|
NORM(unidad) LIKE ? OR
|
||||||
|
NORM(denominacion) LIKE ? OR
|
||||||
|
NORM(situacion) LIKE ?
|
||||||
|
)''')
|
||||||
|
params.extend([like] * 9)
|
||||||
|
|
||||||
where = ' AND '.join(condiciones)
|
where = ' AND '.join(condiciones)
|
||||||
|
|
||||||
rows = db.execute(
|
rows = db.execute(
|
||||||
f'SELECT id, m_oficial, m_civil, marca, modelo, poblacion, unidad, grupo, tipo, situacion FROM vehiculos WHERE {where} {filtro_bajas} ORDER BY id',
|
f'''SELECT id, m_oficial, m_reservada, marca, modelo, poblacion, unidad,
|
||||||
|
denominacion, tipo, situacion, lanzadestellos
|
||||||
|
FROM vehiculos
|
||||||
|
WHERE {where} {filtro_bajas}
|
||||||
|
ORDER BY id''',
|
||||||
params
|
params
|
||||||
).fetchall()
|
).fetchall()
|
||||||
|
|
||||||
else: # PARA QUE LA LISTA SALGA EN BLANCO INICIALMENTE
|
else: # PARA QUE LA LISTA SALGA EN BLANCO INICIALMENTE
|
||||||
rows = []
|
rows = []
|
||||||
# else:
|
# else:
|
||||||
# rows = db.execute(
|
# rows = db.execute(
|
||||||
# '''SELECT id, m_oficial, m_civil, marca, modelo, poblacion, unidad, tipo, tipo, situacion
|
# '''SELECT id, m_oficial, m_reservada, marca, modelo, poblacion, unidad, tipo, tipo, situacion
|
||||||
# FROM vehiculos
|
# FROM vehiculos
|
||||||
# WHERE situacion IS NULL OR UPPER(situacion) != 'BAJA'
|
# WHERE situacion IS NULL OR UPPER(situacion) != 'BAJA'
|
||||||
# ORDER BY id'''
|
# ORDER BY id'''
|
||||||
@@ -266,6 +269,7 @@ def vehiculos_list():
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/vehiculos/nuevo', methods=['GET', 'POST'])
|
@app.route('/vehiculos/nuevo', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def vehiculos_nuevo():
|
def vehiculos_nuevo():
|
||||||
@@ -282,15 +286,14 @@ def vehiculos_nuevo():
|
|||||||
|
|
||||||
cursor = db.execute('''
|
cursor = db.execute('''
|
||||||
INSERT INTO vehiculos (
|
INSERT INTO vehiculos (
|
||||||
m_oficial, m_civil, m_propia, VIN, poliza, viat, legajo, marca, modelo,
|
m_oficial, m_reservada, m_propia, VIN, poliza, viat, legajo, marca, modelo,
|
||||||
tipo, color, cilindrada, potencia, combustible, neumaticos, plazas, lanzadestellos
|
tipo, color, combustible, plazas, lanzadestellos
|
||||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||||
''', (
|
''', (
|
||||||
f.get('m_oficial'), f.get('m_civil'), f.get('m_propia'), f.get('VIN'),
|
f.get('m_oficial'), f.get('m_reservada'), f.get('m_propia'), f.get('VIN'),
|
||||||
f.get('poliza'), f.get('viat'), f.get('legajo') or None,
|
f.get('poliza'), f.get('viat'), f.get('legajo') or None,
|
||||||
f.get('marca'), f.get('modelo'), f.get('tipo'), f.get('color'),
|
f.get('marca'), f.get('modelo'), f.get('tipo'), f.get('color'),
|
||||||
f.get('cilindrada'), f.get('potencia') or None, f.get('combustible'),
|
f.get('combustible'), f.get('plazas'), lanzadestellos
|
||||||
f.get('neumaticos'), f.get('plazas'), lanzadestellos
|
|
||||||
))
|
))
|
||||||
# nuevo_id = cursor.lastrowid
|
# nuevo_id = cursor.lastrowid
|
||||||
db.commit()
|
db.commit()
|
||||||
@@ -347,15 +350,15 @@ def vehiculos_editar(id):
|
|||||||
|
|
||||||
db.execute('''
|
db.execute('''
|
||||||
UPDATE vehiculos SET
|
UPDATE vehiculos SET
|
||||||
m_oficial=?, m_civil=?, m_propia=?, VIN=?, poliza=?, viat=?, legajo=?,
|
m_oficial=?, m_reservada=?, m_propia=?, VIN=?, poliza=?, viat=?, legajo=?,
|
||||||
marca=?, modelo=?, tipo=?, color=?, cilindrada=?, potencia=?, combustible=?, neumaticos=?, plazas=?, lanzadestellos=?,
|
marca=?, modelo=?, tipo=?, color=?, combustible=?, plazas=?, lanzadestellos=?,
|
||||||
situacion=?, poblacion=?, unidad=?,
|
situacion=?, poblacion=?, unidad=?,
|
||||||
int_operacion=?, int_grupo=?, int_juzgado=?, int_procedimiento=?,
|
int_operacion=?, int_grupo=?, int_juzgado=?, int_procedimiento=?,
|
||||||
km=?, km_1=?, itv_ultima=?, itv=?, notas=?
|
km=?, km_1=?, itv_ultima=?, itv=?, notas=?
|
||||||
WHERE id=?
|
WHERE id=?
|
||||||
''', (
|
''', (
|
||||||
f.get('m_oficial'), f.get('m_civil'), f.get('m_propia'), f.get('VIN'), f.get('poliza'), f.get('viat') or '', f.get('legajo') or None,
|
f.get('m_oficial'), f.get('m_reservada'), f.get('m_propia'), f.get('VIN'), f.get('poliza'), f.get('viat') or '', f.get('legajo') or None,
|
||||||
f.get('marca'), f.get('modelo'), f.get('tipo'), f.get('color'), f.get('cilindrada'), f.get('potencia') or None, f.get('combustible'), f.get('neumaticos'), f.get('plazas'), lanzadestellos,
|
f.get('marca'), f.get('modelo'), f.get('tipo'), f.get('color'), f.get('combustible'), f.get('plazas'), lanzadestellos,
|
||||||
f.get('situacion'), f.get('poblacion'), f.get('unidad'),
|
f.get('situacion'), f.get('poblacion'), f.get('unidad'),
|
||||||
f.get('int_operacion'), f.get('int_grupo'), f.get('int_juzgado'), f.get('int_procedimiento'),
|
f.get('int_operacion'), f.get('int_grupo'), f.get('int_juzgado'), f.get('int_procedimiento'),
|
||||||
f.get('km') or None, f.get('km_1') or None, f.get('itv_ultima') or None, f.get('itv') or None,
|
f.get('km') or None, f.get('km_1') or None, f.get('itv_ultima') or None, f.get('itv') or None,
|
||||||
@@ -372,7 +375,12 @@ def vehiculos_editar(id):
|
|||||||
return redirect(f'/viat?q={q_viat}')
|
return redirect(f'/viat?q={q_viat}')
|
||||||
return redirect(url_for('vehiculos_list', q=q) if q else url_for('vehiculos_list'))
|
return redirect(url_for('vehiculos_list', q=q) if q else url_for('vehiculos_list'))
|
||||||
|
|
||||||
matricula = vehiculo['m_oficial'] or vehiculo['m_civil'] or ''
|
documentos = db.execute(
|
||||||
|
'SELECT * FROM documentos WHERE vehiculo_id=? ORDER BY fecha DESC',
|
||||||
|
(id,)
|
||||||
|
).fetchall()
|
||||||
|
|
||||||
|
matricula = vehiculo['m_oficial'] or vehiculo['m_reservada'] or ''
|
||||||
return render_template('vehiculos/form.html',
|
return render_template('vehiculos/form.html',
|
||||||
page='vehiculos',
|
page='vehiculos',
|
||||||
vehiculo=vehiculo,
|
vehiculo=vehiculo,
|
||||||
@@ -386,7 +394,8 @@ def vehiculos_editar(id):
|
|||||||
hasta=hasta,
|
hasta=hasta,
|
||||||
q_itv=q_itv,
|
q_itv=q_itv,
|
||||||
origen=origen,
|
origen=origen,
|
||||||
q_viat=q_viat)
|
q_viat=q_viat,
|
||||||
|
documentos=documentos)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/vehiculos/<int:id>/eliminar', methods=['POST'])
|
@app.route('/vehiculos/<int:id>/eliminar', methods=['POST'])
|
||||||
@@ -523,7 +532,6 @@ def historial_eliminar(vid, hid):
|
|||||||
|
|
||||||
# 6. Si no queda ningún historial, limpiar el vehículo
|
# 6. Si no queda ningún historial, limpiar el vehículo
|
||||||
else:
|
else:
|
||||||
|
|
||||||
db.execute('''
|
db.execute('''
|
||||||
UPDATE vehiculos
|
UPDATE vehiculos
|
||||||
SET
|
SET
|
||||||
@@ -563,33 +571,50 @@ def vehiculos_exportar():
|
|||||||
filtro_bajas = '' if incl_bajas else "AND (situacion IS NULL OR UPPER(situacion) != 'BAJA')"
|
filtro_bajas = '' if incl_bajas else "AND (situacion IS NULL OR UPPER(situacion) != 'BAJA')"
|
||||||
terminos = [t.strip() for t in q.split(',') if t.strip()] if q else []
|
terminos = [t.strip() for t in q.split(',') if t.strip()] if q else []
|
||||||
|
|
||||||
|
|
||||||
|
# terminos = [t.strip() for t in q.split(',') if t.strip()] if q else []
|
||||||
|
|
||||||
|
# Filtro base de bajas
|
||||||
|
filtro_bajas = '' if incl_bajas else "AND (situacion IS NULL OR UPPER(situacion) != 'BAJA')"
|
||||||
|
|
||||||
if terminos:
|
if terminos:
|
||||||
condiciones = []
|
condiciones = []
|
||||||
params = []
|
params = []
|
||||||
|
|
||||||
for t in terminos:
|
for t in terminos:
|
||||||
like = f'%{t}%'
|
t_norm = normalizar(t)
|
||||||
|
|
||||||
|
if t_norm == 'lanzadestellos':
|
||||||
|
condiciones.append('lanzadestellos = 1')
|
||||||
|
continue
|
||||||
|
|
||||||
|
like = f'%{t_norm}%'
|
||||||
condiciones.append('''(
|
condiciones.append('''(
|
||||||
m_oficial LIKE ? OR m_civil LIKE ? OR VIN LIKE ?
|
NORM(m_oficial) LIKE ? OR
|
||||||
OR marca LIKE ? OR modelo LIKE ? OR poblacion LIKE ?
|
NORM(m_reservada) LIKE ? OR
|
||||||
OR unidad LIKE ? OR grupo LIKE ? OR tipo LIKE ?
|
NORM(VIN) LIKE ? OR
|
||||||
OR situacion LIKE ? OR int_operacion LIKE ?
|
NORM(marca) LIKE ? OR
|
||||||
|
NORM(modelo) LIKE ? OR
|
||||||
|
NORM(poblacion) LIKE ? OR
|
||||||
|
NORM(unidad) LIKE ? OR
|
||||||
|
NORM(denominacion) LIKE ? OR
|
||||||
|
NORM(situacion) LIKE ?
|
||||||
)''')
|
)''')
|
||||||
|
params.extend([like] * 9)
|
||||||
|
|
||||||
params.extend([like] * 11)
|
|
||||||
where = ' AND '.join(condiciones)
|
where = ' AND '.join(condiciones)
|
||||||
|
|
||||||
# incluir_bajas = any(t.lower() == 'baja' for t in terminos)
|
|
||||||
# if not incluir_bajas:
|
|
||||||
# where = f"(situacion IS NULL OR UPPER(situacion) != 'BAJA') AND ({where})"
|
|
||||||
|
|
||||||
rows = db.execute(
|
rows = db.execute(
|
||||||
f'SELECT m_oficial, m_civil, marca, modelo, poblacion, unidad, grupo, tipo, situacion FROM vehiculos WHERE {where} {filtro_bajas} ORDER BY id',
|
f'''SELECT id, m_oficial, m_reservada, marca, modelo, poblacion, unidad,
|
||||||
|
denominacion, tipo, situacion, lanzadestellos
|
||||||
|
FROM vehiculos
|
||||||
|
WHERE {where} {filtro_bajas}
|
||||||
|
ORDER BY id''',
|
||||||
params
|
params
|
||||||
).fetchall()
|
).fetchall()
|
||||||
else:
|
else:
|
||||||
rows = db.execute(
|
rows = db.execute(
|
||||||
'''SELECT m_oficial, m_civil, marca, modelo, poblacion, unidad, grupo, tipo, situacion
|
'''SELECT m_oficial, m_reservada, marca, modelo, poblacion, unidad, grupo, tipo, situacion
|
||||||
FROM vehiculos
|
FROM vehiculos
|
||||||
WHERE situacion IS NULL OR UPPER(situacion) != 'BAJA'
|
WHERE situacion IS NULL OR UPPER(situacion) != 'BAJA'
|
||||||
ORDER BY id'''
|
ORDER BY id'''
|
||||||
@@ -638,6 +663,202 @@ def vehiculos_exportar():
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# UPLOADS FICHAS TECNICAS PDF
|
||||||
|
def allowed_file(filename):
|
||||||
|
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||||
|
|
||||||
|
|
||||||
|
# @app.errorhandler(RequestEntityTooLarge)
|
||||||
|
# def handle_file_too_large(e):
|
||||||
|
# flash('Error: El archivo pdf no puede superar los 5 MB.', 'error')
|
||||||
|
# return redirect(request.referrer or '/')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/vehiculos/<int:id>/documentos/upload', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def documentos_upload(id):
|
||||||
|
q = request.form.get('_q', '')
|
||||||
|
incl_bajas = request.form.get('_incl_bajas', '0')
|
||||||
|
desde = request.form.get('_desde', '')
|
||||||
|
hasta = request.form.get('_hasta', '')
|
||||||
|
q_itv = request.form.get('_q_itv', '')
|
||||||
|
origen = request.form.get('_origen', '')
|
||||||
|
q_viat = request.form.get('_q_viat', '')
|
||||||
|
nombre = request.form.get('nombre_doc', '').strip()
|
||||||
|
|
||||||
|
if 'pdf' not in request.files:
|
||||||
|
flash('No se seleccionó ningún archivo.', 'error')
|
||||||
|
return redirect(f'/vehiculos/{id}/editar')
|
||||||
|
|
||||||
|
file = request.files['pdf']
|
||||||
|
if file.filename == '' or not allowed_file(file.filename):
|
||||||
|
flash('Solo se permiten archivos PDF.', 'error')
|
||||||
|
return redirect(f'/vehiculos/{id}/editar')
|
||||||
|
|
||||||
|
# Comprobar tamaño máximo: 5 MiB
|
||||||
|
MAX_FILE_SIZE = 5 * 1024 * 1024
|
||||||
|
|
||||||
|
file.seek(0, os.SEEK_END)
|
||||||
|
file_size = file.tell()
|
||||||
|
file.seek(0)
|
||||||
|
|
||||||
|
if file_size > MAX_FILE_SIZE:
|
||||||
|
flash('El archivo PDF no puede superar los 5 MB.', 'error')
|
||||||
|
return redirect(f'/vehiculos/{id}/editar?q={q}')
|
||||||
|
|
||||||
|
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
||||||
|
|
||||||
|
# Nombre único: vehiculo_id_timestamp.pdf
|
||||||
|
import time
|
||||||
|
filename = f'v{id}_{int(time.time())}.pdf'
|
||||||
|
file.save(os.path.join(UPLOAD_FOLDER, filename))
|
||||||
|
|
||||||
|
# Si no puso nombre, usar el nombre original del archivo
|
||||||
|
if not nombre:
|
||||||
|
nombre = secure_filename(file.filename)
|
||||||
|
|
||||||
|
db = get_db()
|
||||||
|
db.execute(
|
||||||
|
'INSERT INTO documentos (vehiculo_id, nombre, filename) VALUES (?,?,?)',
|
||||||
|
(id, nombre, filename)
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
flash(f'Documento «{nombre}» subido correctamente.', 'ok')
|
||||||
|
|
||||||
|
url = f'/vehiculos/{id}/editar?q={q}'
|
||||||
|
if origen == 'viat':
|
||||||
|
url = f'/vehiculos/{id}/editar?origen=viat&q_viat={q_viat}'
|
||||||
|
elif desde or hasta or q_itv:
|
||||||
|
url = f'/vehiculos/{id}/editar?desde={desde}&hasta={hasta}&q_itv={q_itv}'
|
||||||
|
return redirect(url)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/vehiculos/documentos/<int:doc_id>/ver')
|
||||||
|
@login_required
|
||||||
|
def documentos_ver(doc_id):
|
||||||
|
from flask import send_from_directory
|
||||||
|
db = get_db()
|
||||||
|
doc = db.execute('SELECT * FROM documentos WHERE id=?', (doc_id,)).fetchone()
|
||||||
|
if not doc:
|
||||||
|
flash('Documento no encontrado.', 'error')
|
||||||
|
return redirect(url_for('vehiculos_list'))
|
||||||
|
return send_from_directory(UPLOAD_FOLDER, doc['filename'], as_attachment=False)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/vehiculos/documentos/<int:doc_id>/descargar')
|
||||||
|
@login_required
|
||||||
|
def documentos_descargar(doc_id):
|
||||||
|
from flask import send_from_directory
|
||||||
|
db = get_db()
|
||||||
|
doc = db.execute('SELECT * FROM documentos WHERE id=?', (doc_id,)).fetchone()
|
||||||
|
if not doc:
|
||||||
|
flash('Documento no encontrado.', 'error')
|
||||||
|
return redirect(url_for('vehiculos_list'))
|
||||||
|
return send_from_directory(UPLOAD_FOLDER, doc['filename'],
|
||||||
|
as_attachment=True,
|
||||||
|
download_name=doc['nombre'])
|
||||||
|
# download_name=doc['nombre'] + '.pdf')
|
||||||
|
|
||||||
|
@app.route('/vehiculos/documentos/<int:doc_id>/eliminar', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def documentos_eliminar(doc_id):
|
||||||
|
db = get_db()
|
||||||
|
vid = request.form.get('vid', '')
|
||||||
|
q = request.form.get('_q', '')
|
||||||
|
incl_bajas = request.form.get('_incl_bajas', '0')
|
||||||
|
|
||||||
|
doc = db.execute('SELECT * FROM documentos WHERE id=?', (doc_id,)).fetchone()
|
||||||
|
if doc:
|
||||||
|
filepath = os.path.join(UPLOAD_FOLDER, doc['filename'])
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
os.remove(filepath)
|
||||||
|
db.execute('DELETE FROM documentos WHERE id=?', (doc_id,))
|
||||||
|
db.commit()
|
||||||
|
flash('Documento eliminado.', 'ok')
|
||||||
|
|
||||||
|
return redirect(f'/vehiculos/{vid}/editar?q={q}&incl_bajas={incl_bajas}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# @app.route('/vehiculos/<int:id>/upload', methods=['POST'])
|
||||||
|
# @login_required
|
||||||
|
# def vehiculos_upload(id):
|
||||||
|
# q = request.form.get('_q', '')
|
||||||
|
# incl_bajas = request.form.get('_incl_bajas', '0')
|
||||||
|
# desde = request.form.get('_desde', '')
|
||||||
|
# hasta = request.form.get('_hasta', '')
|
||||||
|
# q_itv = request.form.get('_q_itv', '')
|
||||||
|
# origen = request.form.get('_origen', '')
|
||||||
|
# q_viat = request.form.get('_q_viat', '')
|
||||||
|
|
||||||
|
# if 'pdf' not in request.files:
|
||||||
|
# flash('No se seleccionó ningún archivo.', 'error')
|
||||||
|
# return redirect(f'/vehiculos/{id}/editar?q={q}&incl_bajas={incl_bajas}')
|
||||||
|
|
||||||
|
# file = request.files['pdf']
|
||||||
|
# if file.filename == '':
|
||||||
|
# flash('No se seleccionó ningún archivo.', 'error')
|
||||||
|
# return redirect(f'/vehiculos/{id}/editar?q={q}&incl_bajas={incl_bajas}')
|
||||||
|
|
||||||
|
# if file and allowed_file(file.filename):
|
||||||
|
# # Guardar con nombre único basado en id del vehículo
|
||||||
|
# os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
||||||
|
# filename = f'ficha_tecnica_id_{id}.pdf'
|
||||||
|
# filepath = os.path.join(UPLOAD_FOLDER, filename)
|
||||||
|
# file.save(filepath)
|
||||||
|
|
||||||
|
# # Guardar referencia en BD
|
||||||
|
# db = get_db()
|
||||||
|
# db.execute('UPDATE vehiculos SET pdf=? WHERE id=?', (filename, id))
|
||||||
|
# db.commit()
|
||||||
|
# flash('Documento PDF subido correctamente.', 'ok')
|
||||||
|
# else:
|
||||||
|
# flash('Solo se permiten archivos PDF.', 'error')
|
||||||
|
|
||||||
|
# url = f'/vehiculos/{id}/editar?q={q}&incl_bajas={incl_bajas}'
|
||||||
|
# if origen == 'viat':
|
||||||
|
# url = f'/vehiculos/{id}/editar?origen=viat&q_viat={q_viat}'
|
||||||
|
# elif desde or hasta or q_itv:
|
||||||
|
# url = f'/vehiculos/{id}/editar?desde={desde}&hasta={hasta}&q_itv={q_itv}'
|
||||||
|
# return redirect(url)
|
||||||
|
|
||||||
|
|
||||||
|
# @app.route('/vehiculos/<int:id>/pdf')
|
||||||
|
# @login_required
|
||||||
|
# def vehiculos_pdf(id):
|
||||||
|
# from flask import send_from_directory
|
||||||
|
# db = get_db()
|
||||||
|
# vehiculo = db.execute('SELECT pdf FROM vehiculos WHERE id=?', (id,)).fetchone()
|
||||||
|
# if not vehiculo or not vehiculo['pdf']:
|
||||||
|
# flash('No hay documento PDF para este vehículo.', 'error')
|
||||||
|
# return redirect(f'/vehiculos/{id}/editar')
|
||||||
|
# return send_from_directory(UPLOAD_FOLDER, vehiculo['pdf'], as_attachment=False)
|
||||||
|
|
||||||
|
|
||||||
|
# @app.route('/vehiculos/<int:id>/pdf/eliminar', methods=['POST'])
|
||||||
|
# @login_required
|
||||||
|
# def vehiculos_pdf_eliminar(id):
|
||||||
|
# q = request.form.get('_q', '')
|
||||||
|
# incl_bajas = request.form.get('_incl_bajas', '0')
|
||||||
|
# db = get_db()
|
||||||
|
# vehiculo = db.execute('SELECT pdf FROM vehiculos WHERE id=?', (id,)).fetchone()
|
||||||
|
# if vehiculo and vehiculo['pdf']:
|
||||||
|
# filepath = os.path.join(UPLOAD_FOLDER, vehiculo['pdf'])
|
||||||
|
# if os.path.exists(filepath):
|
||||||
|
# os.remove(filepath)
|
||||||
|
# db.execute('UPDATE vehiculos SET pdf=NULL WHERE id=?', (id,))
|
||||||
|
# db.commit()
|
||||||
|
# flash('Documento PDF eliminado.', 'ok')
|
||||||
|
# return redirect(f'/vehiculos/{id}/editar?q={q}&incl_bajas={incl_bajas}')
|
||||||
|
|
||||||
|
|
||||||
# Ruta API que devuelve unidades según población
|
# Ruta API que devuelve unidades según población
|
||||||
@app.route('/api/unidades/<int:poblacion_id>')
|
@app.route('/api/unidades/<int:poblacion_id>')
|
||||||
@@ -683,7 +904,7 @@ def itv_list():
|
|||||||
params.extend([like] * 3)
|
params.extend([like] * 3)
|
||||||
where_q = ' AND '.join(condiciones)
|
where_q = ' AND '.join(condiciones)
|
||||||
rows = db.execute(f'''
|
rows = db.execute(f'''
|
||||||
SELECT id, m_oficial, m_civil, marca, modelo, tipo,
|
SELECT id, m_oficial, m_reservada, marca, modelo, tipo,
|
||||||
tipo, unidad, poblacion, itv
|
tipo, unidad, poblacion, itv
|
||||||
FROM vehiculos
|
FROM vehiculos
|
||||||
WHERE itv IS NOT NULL AND itv != ''
|
WHERE itv IS NOT NULL AND itv != ''
|
||||||
@@ -693,7 +914,7 @@ def itv_list():
|
|||||||
''', params).fetchall()
|
''', params).fetchall()
|
||||||
else:
|
else:
|
||||||
rows = db.execute('''
|
rows = db.execute('''
|
||||||
SELECT id, m_oficial, m_civil, marca, modelo, tipo,
|
SELECT id, m_oficial, m_reservada, marca, modelo, tipo,
|
||||||
tipo, unidad, poblacion, itv
|
tipo, unidad, poblacion, itv
|
||||||
FROM vehiculos
|
FROM vehiculos
|
||||||
WHERE itv IS NOT NULL AND itv != ''
|
WHERE itv IS NOT NULL AND itv != ''
|
||||||
@@ -735,7 +956,7 @@ def itv_exportar():
|
|||||||
params.extend([like] * 3)
|
params.extend([like] * 3)
|
||||||
where_q = ' AND '.join(condiciones)
|
where_q = ' AND '.join(condiciones)
|
||||||
rows = db.execute(f'''
|
rows = db.execute(f'''
|
||||||
SELECT m_oficial, m_civil, marca, modelo, tipo,
|
SELECT m_oficial, m_reservada, marca, modelo, tipo,
|
||||||
tipo, unidad, poblacion, itv
|
tipo, unidad, poblacion, itv
|
||||||
FROM vehiculos
|
FROM vehiculos
|
||||||
WHERE itv IS NOT NULL AND itv != ''
|
WHERE itv IS NOT NULL AND itv != ''
|
||||||
@@ -745,7 +966,7 @@ def itv_exportar():
|
|||||||
''', params).fetchall()
|
''', params).fetchall()
|
||||||
else:
|
else:
|
||||||
rows = db.execute('''
|
rows = db.execute('''
|
||||||
SELECT m_oficial, m_civil, marca, modelo, tipo,
|
SELECT m_oficial, m_reservada, marca, modelo, tipo,
|
||||||
tipo, unidad, poblacion, itv
|
tipo, unidad, poblacion, itv
|
||||||
FROM vehiculos
|
FROM vehiculos
|
||||||
WHERE itv IS NOT NULL AND itv != ''
|
WHERE itv IS NOT NULL AND itv != ''
|
||||||
@@ -941,6 +1162,104 @@ def editar_unidades():
|
|||||||
# return redirect(url_for('unidades_list'))
|
# return redirect(url_for('unidades_list'))
|
||||||
|
|
||||||
|
|
||||||
|
############### API PARA USAR TABULATOR ######################################
|
||||||
|
|
||||||
|
# @app.route("/api/unidades/<int:unidad_id>", methods=["PATCH"])
|
||||||
|
# @login_required
|
||||||
|
# def actualizar_unidad(unidad_id):
|
||||||
|
|
||||||
|
# datos = request.get_json(silent=True) or {}
|
||||||
|
|
||||||
|
# campo = datos.get("campo")
|
||||||
|
# valor = datos.get("valor")
|
||||||
|
|
||||||
|
# # Solo permitimos estos campos
|
||||||
|
# if campo not in ("poblacion_id", "unidad"):
|
||||||
|
# return jsonify({
|
||||||
|
# "error": "Campo no permitido"
|
||||||
|
# }), 400
|
||||||
|
|
||||||
|
# db = get_db()
|
||||||
|
|
||||||
|
# # Comprobar que existe la unidad
|
||||||
|
# unidad_actual = db.execute(
|
||||||
|
# "SELECT id FROM unidades WHERE id=?",
|
||||||
|
# (unidad_id,)
|
||||||
|
# ).fetchone()
|
||||||
|
|
||||||
|
# if unidad_actual is None:
|
||||||
|
|
||||||
|
# return jsonify({
|
||||||
|
# "error": "La unidad no existe"
|
||||||
|
# }), 404
|
||||||
|
|
||||||
|
# # CAMBIO DE POBLACIÓN
|
||||||
|
# if campo == "poblacion_id":
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# poblacion_id = int(valor)
|
||||||
|
# except (ValueError, TypeError):
|
||||||
|
|
||||||
|
# return jsonify({
|
||||||
|
# "error": "Población no válida"
|
||||||
|
# }), 400
|
||||||
|
|
||||||
|
|
||||||
|
# poblacion = db.execute(
|
||||||
|
# """ SELECT id, poblacion FROM poblaciones WHERE id=? """,
|
||||||
|
# (poblacion_id,)
|
||||||
|
# ).fetchone()
|
||||||
|
|
||||||
|
# if poblacion is None:
|
||||||
|
|
||||||
|
# return jsonify({
|
||||||
|
# "error": "La población no existe"
|
||||||
|
# }), 400
|
||||||
|
|
||||||
|
# valor = poblacion_id
|
||||||
|
|
||||||
|
# # CAMBIO DE UNIDAD
|
||||||
|
# elif campo == "unidad":
|
||||||
|
|
||||||
|
# valor = str(valor or "").strip()
|
||||||
|
|
||||||
|
# if not valor:
|
||||||
|
|
||||||
|
# return jsonify({
|
||||||
|
# "error": "El nombre de la unidad no puede estar vacío"
|
||||||
|
# }), 400
|
||||||
|
|
||||||
|
# # UPDATE
|
||||||
|
# db.execute(f""" UPDATE unidades SET {campo}=? WHERE id=?""",
|
||||||
|
# (valor, unidad_id)
|
||||||
|
# )
|
||||||
|
|
||||||
|
# db.commit()
|
||||||
|
|
||||||
|
# # DEVOLVER EL REGISTRO ACTUALIZADO
|
||||||
|
# fila = db.execute(
|
||||||
|
# """
|
||||||
|
# SELECT
|
||||||
|
# u.id,
|
||||||
|
# u.poblacion_id,
|
||||||
|
# u.unidad,
|
||||||
|
# p.poblacion
|
||||||
|
# FROM unidades u
|
||||||
|
# LEFT JOIN poblaciones p
|
||||||
|
# ON u.poblacion_id = p.id
|
||||||
|
# WHERE u.id=? """,
|
||||||
|
# (unidad_id,)
|
||||||
|
# ).fetchone()
|
||||||
|
|
||||||
|
|
||||||
|
# return jsonify({
|
||||||
|
# "id": fila["id"],
|
||||||
|
# "poblacion_id": fila["poblacion_id"],
|
||||||
|
# "poblacion": fila["poblacion"],
|
||||||
|
# "unidad": fila["unidad"]
|
||||||
|
# })
|
||||||
|
|
||||||
|
|
||||||
# ── CRUD tipo ─────────────────────────────────────────
|
# ── CRUD tipo ─────────────────────────────────────────
|
||||||
@app.route('/tipo')
|
@app.route('/tipo')
|
||||||
@login_required
|
@login_required
|
||||||
@@ -1023,7 +1342,7 @@ def viat_list():
|
|||||||
terminos = [t.strip() for t in q.split(',') if t.strip()] if q else []
|
terminos = [t.strip() for t in q.split(',') if t.strip()] if q else []
|
||||||
|
|
||||||
sql = '''
|
sql = '''
|
||||||
SELECT id, viat, m_oficial, m_civil, marca, modelo,
|
SELECT id, viat, m_oficial, m_reservada, marca, modelo,
|
||||||
poblacion, unidad, tipo
|
poblacion, unidad, tipo
|
||||||
FROM vehiculos
|
FROM vehiculos
|
||||||
WHERE viat IS NOT NULL
|
WHERE viat IS NOT NULL
|
||||||
@@ -1040,7 +1359,7 @@ def viat_list():
|
|||||||
(
|
(
|
||||||
viat LIKE ?
|
viat LIKE ?
|
||||||
OR m_oficial LIKE ?
|
OR m_oficial LIKE ?
|
||||||
OR m_civil LIKE ?
|
OR m_reservada LIKE ?
|
||||||
OR marca LIKE ?
|
OR marca LIKE ?
|
||||||
OR modelo LIKE ?
|
OR modelo LIKE ?
|
||||||
OR poblacion LIKE ?
|
OR poblacion LIKE ?
|
||||||
@@ -1076,7 +1395,7 @@ def viat_exportar():
|
|||||||
terminos = [t.strip() for t in q.split(',') if t.strip()] if q else []
|
terminos = [t.strip() for t in q.split(',') if t.strip()] if q else []
|
||||||
|
|
||||||
sql = '''
|
sql = '''
|
||||||
SELECT id, viat, m_oficial, m_civil, marca, modelo,
|
SELECT id, viat, m_oficial, m_reservada, marca, modelo,
|
||||||
poblacion, unidad, tipo
|
poblacion, unidad, tipo
|
||||||
FROM vehiculos
|
FROM vehiculos
|
||||||
WHERE viat IS NOT NULL
|
WHERE viat IS NOT NULL
|
||||||
@@ -1093,7 +1412,7 @@ def viat_exportar():
|
|||||||
(
|
(
|
||||||
viat LIKE ?
|
viat LIKE ?
|
||||||
OR m_oficial LIKE ?
|
OR m_oficial LIKE ?
|
||||||
OR m_civil LIKE ?
|
OR m_reservada LIKE ?
|
||||||
OR marca LIKE ?
|
OR marca LIKE ?
|
||||||
OR modelo LIKE ?
|
OR modelo LIKE ?
|
||||||
OR poblacion LIKE ?
|
OR poblacion LIKE ?
|
||||||
@@ -1126,7 +1445,7 @@ def viat_exportar():
|
|||||||
ws.row_dimensions[1].height = 22
|
ws.row_dimensions[1].height = 22
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
ws.append([row['viat'], row['m_oficial'], row['m_civil'],
|
ws.append([row['viat'], row['m_oficial'], row['m_reservada'],
|
||||||
row['marca'], row['modelo'], row['poblacion'],
|
row['marca'], row['modelo'], row['poblacion'],
|
||||||
row['unidad'], row['tipo']])
|
row['unidad'], row['tipo']])
|
||||||
|
|
||||||
@@ -1285,9 +1604,35 @@ def fecha_es(valor):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
init_db()
|
init_db()
|
||||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||||
# app.run(debug=True)
|
# app.run(debug=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# CODIGO PREVIO A INCLUIR NORMALIZACION
|
||||||
|
# if terminos:
|
||||||
|
# condiciones = []
|
||||||
|
# params = []
|
||||||
|
# for t in terminos:
|
||||||
|
# like = f'%{t}%'
|
||||||
|
# condiciones.append('''(
|
||||||
|
# m_oficial LIKE ? OR m_reservada LIKE ? OR VIN LIKE ?
|
||||||
|
# OR marca LIKE ? OR modelo LIKE ? OR poblacion LIKE ?
|
||||||
|
# OR unidad LIKE ? OR grupo LIKE ? OR tipo LIKE ?
|
||||||
|
# OR situacion LIKE ? OR int_operacion LIKE ?
|
||||||
|
# )''')
|
||||||
|
|
||||||
|
# params.extend([like] * 11)
|
||||||
|
# where = ' AND '.join(condiciones)
|
||||||
|
|
||||||
|
# rows = db.execute(
|
||||||
|
# f'SELECT id, m_oficial, m_reservada, marca, modelo, poblacion, unidad, grupo, tipo, situacion FROM vehiculos WHERE {where} {filtro_bajas} ORDER BY id',
|
||||||
|
# params
|
||||||
|
# ).fetchall()
|
||||||
|
|
||||||
|
|||||||
@@ -11,15 +11,46 @@ navbar_vertical_CRUD/
|
|||||||
└── form.html
|
└── form.html
|
||||||
|
|
||||||
|
|
||||||
|
Adminer acceso
|
||||||
|
http://10.76.243.137:5001/
|
||||||
|
System: SQLite 3
|
||||||
|
Username: <empty>
|
||||||
|
Password: p4ssw0rd
|
||||||
|
Database: /db/pyfleet.db
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
python app.py
|
python app.py
|
||||||
# → http://127.0.0.1:5000
|
# → http://127.0.0.1:5000
|
||||||
|
|
||||||
|
gunicorn -w 2 -b 0.0.0.0:5000 app:app
|
||||||
|
|
||||||
|
Ejecutar con Python en segundo plano
|
||||||
|
nohup python app.py > flask.log 2>&1 &
|
||||||
|
Comprobar que esta activo
|
||||||
|
ps aux | grep python
|
||||||
|
Para detenerlo
|
||||||
|
pkill -f "Python app.py"
|
||||||
|
|
||||||
|
Gunicorn en segundo plano.
|
||||||
|
gunicorn -w 4 -b 0.0.0.0:5000 app:app > gunicorn.log 2>&1 &
|
||||||
|
nohup gunicorn -w 4 -b 127.0.0.1:5000 app:app > gunicorn.log 2>&1 &
|
||||||
|
Parar
|
||||||
|
ps aux | grep gunicorn (averiguar proceso)
|
||||||
|
pkill -f gunicorn. (nombre proceso)
|
||||||
|
kill 12345. (Numero proceso)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PRODUCCION LINUX GUNICORN
|
PRODUCCION LINUX GUNICORN
|
||||||
pip install gunicorn
|
pip install gunicorn
|
||||||
gunicorn app:app
|
gunicorn app:app
|
||||||
gunicorn --bind 0.0.0.0:8000 app:app
|
gunicorn -w 2 -b 0.0.0.0:5000 app:app
|
||||||
|
gunicorn --bind 0.0.0.0:5000 app:app
|
||||||
|
|
||||||
|
gunicorn -w 4 -b 127.0.0.1:8000 app:app
|
||||||
gunicorn --workers 4 --bind 0.0.0.0:8000 app:app
|
gunicorn --workers 4 --bind 0.0.0.0:8000 app:app
|
||||||
gunicorn --workers 4 --bind 0.0.0.0:8000 --timeout 120 app:app
|
gunicorn --workers 4 --bind 0.0.0.0:8000 --timeout 120 app:app
|
||||||
|
|
||||||
@@ -36,6 +67,7 @@ kill 1337144 la primera de la lista
|
|||||||
pkill -f "gunicorn.*app:app lo elimina de una sola vez
|
pkill -f "gunicorn.*app:app lo elimina de una sola vez
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
|
|
||||||
TABLAS
|
TABLAS
|
||||||
@@ -43,7 +75,7 @@ TABLAS
|
|||||||
CREATE TABLE "vehiculos" (
|
CREATE TABLE "vehiculos" (
|
||||||
"id" INTEGER,
|
"id" INTEGER,
|
||||||
"m_oficial" TEXT,
|
"m_oficial" TEXT,
|
||||||
"m_civil" TEXT,
|
"m_reservada" TEXT,
|
||||||
"m_propia" TEXT,
|
"m_propia" TEXT,
|
||||||
"VIN" TEXT,
|
"VIN" TEXT,
|
||||||
"poliza" TEXT,
|
"poliza" TEXT,
|
||||||
@@ -54,10 +86,7 @@ CREATE TABLE "vehiculos" (
|
|||||||
"tipo" TEXT,
|
"tipo" TEXT,
|
||||||
"denominacion" TEXT,
|
"denominacion" TEXT,
|
||||||
"color" REAL,
|
"color" REAL,
|
||||||
"cilindrada" TEXT,
|
|
||||||
"potencia" TEXT,
|
|
||||||
"combustible" TEXT,
|
"combustible" TEXT,
|
||||||
"neumaticos" TEXT,
|
|
||||||
"plazas" TEXT,
|
"plazas" TEXT,
|
||||||
"lanzadestellos" INTEGER,
|
"lanzadestellos" INTEGER,
|
||||||
"situacion" TEXT,
|
"situacion" TEXT,
|
||||||
@@ -77,11 +106,53 @@ CREATE TABLE "vehiculos" (
|
|||||||
"itv" INTEGER,
|
"itv" INTEGER,
|
||||||
"observaciones" TEXT,
|
"observaciones" TEXT,
|
||||||
"historial" TEXT,
|
"historial" TEXT,
|
||||||
"notas" TEXT,
|
"notas" TEXT, pdf TEXT,
|
||||||
PRIMARY KEY("id" AUTOINCREMENT)
|
PRIMARY KEY("id" AUTOINCREMENT)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE documentos (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
vehiculo_id INTEGER NOT NULL REFERENCES vehiculos(id) ON DELETE CASCADE,
|
||||||
|
nombre TEXT,
|
||||||
|
filename TEXT NOT NULL,
|
||||||
|
fecha DATETIME DEFAULT (datetime('now','localtime'))
|
||||||
|
)
|
||||||
|
|
||||||
|
CREATE TABLE "historial" (
|
||||||
|
"id" INTEGER,
|
||||||
|
"vehiculo_id" INTEGER,
|
||||||
|
"fecha" TEXT,
|
||||||
|
"matricula" TEXT,
|
||||||
|
"situacion" TEXT,
|
||||||
|
"poblacion" TEXT,
|
||||||
|
"unidad" TEXT,
|
||||||
|
"grupo" TEXT,
|
||||||
|
"observaciones" TEXT,
|
||||||
|
PRIMARY KEY("id" AUTOINCREMENT),
|
||||||
|
FOREIGN KEY("vehiculo_id") REFERENCES "vehiculos"("id")
|
||||||
|
)
|
||||||
|
|
||||||
|
CREATE TABLE mensajes (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
texto TEXT NOT NULL,
|
||||||
|
autor TEXT,
|
||||||
|
fecha DATETIME DEFAULT (datetime('now','localtime'))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
Cambiar nombre de columna
|
||||||
|
|
||||||
|
ALTER TABLE nombre_tabla
|
||||||
|
RENAME COLUMN nombre_actual TO nombre_nuevo;
|
||||||
|
|
||||||
|
Eliminar columna (hay que hacerlo de una en una (SQLite no permite varias a la vez)
|
||||||
|
ALTER TABLE usuarios
|
||||||
|
DROP COLUMN nombre;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Cambiar None por Null table vehiculos
|
Cambiar None por Null table vehiculos
|
||||||
UPDATE vehiculos
|
UPDATE vehiculos
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 476 B After Width: | Height: | Size: 476 B |
|
Before Width: | Height: | Size: 507 B After Width: | Height: | Size: 507 B |
|
Before Width: | Height: | Size: 514 B After Width: | Height: | Size: 514 B |
|
Before Width: | Height: | Size: 579 B After Width: | Height: | Size: 579 B |
|
Before Width: | Height: | Size: 250 B After Width: | Height: | Size: 250 B |
|
Before Width: | Height: | Size: 279 B After Width: | Height: | Size: 279 B |
|
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 286 B |
|
Before Width: | Height: | Size: 366 B After Width: | Height: | Size: 366 B |
|
Before Width: | Height: | Size: 854 B After Width: | Height: | Size: 854 B |
|
Before Width: | Height: | Size: 457 B After Width: | Height: | Size: 457 B |
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 477 B |
|
Before Width: | Height: | Size: 484 B After Width: | Height: | Size: 484 B |
|
Before Width: | Height: | Size: 564 B After Width: | Height: | Size: 564 B |
|
Before Width: | Height: | Size: 607 B After Width: | Height: | Size: 607 B |
|
Before Width: | Height: | Size: 642 B After Width: | Height: | Size: 642 B |
|
Before Width: | Height: | Size: 634 B After Width: | Height: | Size: 634 B |
|
Before Width: | Height: | Size: 714 B After Width: | Height: | Size: 714 B |
|
Before Width: | Height: | Size: 359 B After Width: | Height: | Size: 359 B |
|
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 421 B |
|
Before Width: | Height: | Size: 428 B After Width: | Height: | Size: 428 B |
|
Before Width: | Height: | Size: 493 B After Width: | Height: | Size: 493 B |
|
Before Width: | Height: | Size: 495 B After Width: | Height: | Size: 495 B |
|
Before Width: | Height: | Size: 514 B After Width: | Height: | Size: 514 B |
|
Before Width: | Height: | Size: 521 B After Width: | Height: | Size: 521 B |
|
Before Width: | Height: | Size: 601 B After Width: | Height: | Size: 601 B |
|
Before Width: | Height: | Size: 617 B After Width: | Height: | Size: 617 B |
|
Before Width: | Height: | Size: 640 B After Width: | Height: | Size: 640 B |
|
Before Width: | Height: | Size: 662 B After Width: | Height: | Size: 662 B |
|
Before Width: | Height: | Size: 727 B After Width: | Height: | Size: 727 B |
|
Before Width: | Height: | Size: 251 B After Width: | Height: | Size: 251 B |
|
Before Width: | Height: | Size: 279 B After Width: | Height: | Size: 279 B |
|
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 286 B |
|
Before Width: | Height: | Size: 366 B After Width: | Height: | Size: 366 B |
|
Before Width: | Height: | Size: 686 B After Width: | Height: | Size: 686 B |
|
Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 717 B |
|
Before Width: | Height: | Size: 737 B After Width: | Height: | Size: 737 B |
|
Before Width: | Height: | Size: 804 B After Width: | Height: | Size: 804 B |
|
Before Width: | Height: | Size: 574 B After Width: | Height: | Size: 574 B |
|
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 597 B |
|
Before Width: | Height: | Size: 620 B After Width: | Height: | Size: 620 B |
|
Before Width: | Height: | Size: 684 B After Width: | Height: | Size: 684 B |
|
Before Width: | Height: | Size: 366 B After Width: | Height: | Size: 366 B |
|
Before Width: | Height: | Size: 687 B After Width: | Height: | Size: 687 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 492 B After Width: | Height: | Size: 492 B |
|
Before Width: | Height: | Size: 840 B After Width: | Height: | Size: 840 B |
|
Before Width: | Height: | Size: 615 B After Width: | Height: | Size: 615 B |
|
Before Width: | Height: | Size: 689 B After Width: | Height: | Size: 689 B |
|
Before Width: | Height: | Size: 436 B After Width: | Height: | Size: 436 B |
|
Before Width: | Height: | Size: 264 B After Width: | Height: | Size: 264 B |
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 311 B |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 311 B |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 281 B |
|
Before Width: | Height: | Size: 839 B After Width: | Height: | Size: 839 B |
|
Before Width: | Height: | Size: 845 B After Width: | Height: | Size: 845 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 324 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 230 B |
|
Before Width: | Height: | Size: 432 B After Width: | Height: | Size: 432 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 311 B |
|
Before Width: | Height: | Size: 382 B After Width: | Height: | Size: 382 B |
|
Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 278 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 394 B After Width: | Height: | Size: 394 B |
|
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 345 B |
|
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 346 B |
|
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 373 B After Width: | Height: | Size: 373 B |
|
Before Width: | Height: | Size: 373 B After Width: | Height: | Size: 373 B |
|
Before Width: | Height: | Size: 373 B After Width: | Height: | Size: 373 B |
|
Before Width: | Height: | Size: 374 B After Width: | Height: | Size: 374 B |
|
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 356 B After Width: | Height: | Size: 356 B |
|
Before Width: | Height: | Size: 316 B After Width: | Height: | Size: 316 B |
|
Before Width: | Height: | Size: 364 B After Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 313 B |
|
Before Width: | Height: | Size: 363 B After Width: | Height: | Size: 363 B |
|
Before Width: | Height: | Size: 360 B After Width: | Height: | Size: 360 B |
|
Before Width: | Height: | Size: 435 B After Width: | Height: | Size: 435 B |