├── apps ├── __init__.py ├── libro │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0003_auto_20210719_1251.py │ │ ├── 0002_auto_20210718_1323.py │ │ └── 0001_initial.py │ ├── apps.py │ ├── formsets.py │ ├── views_function.py │ ├── admin.py │ ├── urls.py │ ├── forms.py │ └── models.py ├── usuario │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ └── 0001_initial.py │ ├── templatetags │ │ ├── __init__.py │ │ └── has_group.py │ ├── apps.py │ ├── admin.py │ ├── middleware.py │ ├── urls.py │ ├── mixins.py │ ├── forms.py │ ├── models.py │ └── views.py └── __pycache__ │ ├── __init__.cpython-36.pyc │ └── __init__.cpython-37.pyc ├── tests ├── __init__.py ├── providers │ ├── user_providers.py │ ├── libro_providers.py │ └── general_providers.py ├── test_libro.py ├── factories.py └── test_user.py ├── biblioteca ├── __init__.py ├── __pycache__ │ ├── urls.cpython-36.pyc │ ├── urls.cpython-37.pyc │ ├── wsgi.cpython-36.pyc │ ├── wsgi.cpython-37.pyc │ ├── __init__.cpython-36.pyc │ ├── __init__.cpython-37.pyc │ ├── settings.cpython-36.pyc │ └── settings.cpython-37.pyc ├── wsgi.py ├── urls.py └── settings.py ├── static ├── images │ ├── .gitignore │ ├── logo.png │ ├── logo.psd │ ├── admin.jpg │ ├── admin.png │ ├── admin.pong │ ├── favicon.png │ ├── logo2.png │ └── avatar │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── 4.jpg │ │ ├── 5.jpg │ │ ├── 6.jpg │ │ ├── 64-1.jpg │ │ └── 64-2.jpg ├── fonts │ └── icomoon │ │ ├── index.html │ │ ├── icomoon.eot │ │ ├── icomoon.ttf │ │ ├── icomoon.woff │ │ └── icomoon.svg ├── fullcalendar-5.3.2.zip ├── css │ ├── lib │ │ ├── chosen │ │ │ ├── chosen-sprite.png │ │ │ └── chosen-sprite@2x.png │ │ ├── vector-map │ │ │ └── jqvmap.min.css │ │ └── datatable │ │ │ ├── dataTables.bootstrap.min.css │ │ │ ├── buttons.bootstrap.min.css │ │ │ └── buttons.dataTables.min.css │ └── cs-skin-elastic.css └── js │ ├── lib │ └── data-table │ │ ├── buttons.bootstrap.min.js │ │ ├── buttons.print.min.js │ │ ├── dataTables.bootstrap.min.js │ │ └── buttons.colVis.min.js │ ├── init │ ├── datatables-init.js │ ├── peitychart-init.js │ ├── weather-init.js │ ├── gmap-init.js │ └── vector-init.js │ ├── widgets.js │ ├── libro │ ├── reservas.js │ ├── reservas_vencidas.js │ ├── libro.js │ └── autor.js │ ├── vmap.sampledata.js │ ├── main.js │ └── usuarios │ └── index.js ├── docs ├── .readthedocs.yaml ├── mkdocs.yml ├── index.rst ├── authenticated.md ├── authenticated.rst ├── about.md ├── index.md ├── about.rst └── introduction.rst ├── pytest.ini ├── conftest.py ├── templates ├── libro │ ├── autor_confirm_delete.html │ ├── libro_confirm_delete.html │ ├── autor │ │ ├── eliminar_autor.html │ │ ├── autor.html │ │ ├── crear_autor.html │ │ ├── listar_autor.html │ │ └── autor_formset.html │ ├── libro │ │ ├── eliminar_libro.html │ │ ├── crear_libro.html │ │ ├── libro.html │ │ └── listar_libro.html │ ├── reservas_vencidas.html │ ├── libros_reservados.html │ ├── libros_disponibles.html │ └── detalle_libro_disponible.html ├── usuarios │ ├── eliminar_usuario.html │ ├── crear_usuario.html │ ├── editar_usuario.html │ ├── cambiar_password.html │ └── listar_usuario.html ├── login.html └── index.html ├── manage.py ├── requirements.txt ├── convenciones_comentarios_codigo.txt ├── population.py └── .gitignore /apps/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/libro/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /biblioteca/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/usuario/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/images/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/libro/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/fonts/icomoon/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/providers/user_providers.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/usuario/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/usuario/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/providers/libro_providers.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | mkdocs: 4 | configuration: mkdocs.yml -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/logo.png -------------------------------------------------------------------------------- /static/images/logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/logo.psd -------------------------------------------------------------------------------- /static/images/admin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/admin.jpg -------------------------------------------------------------------------------- /static/images/admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/admin.png -------------------------------------------------------------------------------- /static/images/admin.pong: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/admin.pong -------------------------------------------------------------------------------- /static/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/favicon.png -------------------------------------------------------------------------------- /static/images/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/logo2.png -------------------------------------------------------------------------------- /static/images/avatar/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/avatar/1.jpg -------------------------------------------------------------------------------- /static/images/avatar/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/avatar/2.jpg -------------------------------------------------------------------------------- /static/images/avatar/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/avatar/3.jpg -------------------------------------------------------------------------------- /static/images/avatar/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/avatar/4.jpg -------------------------------------------------------------------------------- /static/images/avatar/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/avatar/5.jpg -------------------------------------------------------------------------------- /static/images/avatar/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/avatar/6.jpg -------------------------------------------------------------------------------- /static/fullcalendar-5.3.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/fullcalendar-5.3.2.zip -------------------------------------------------------------------------------- /static/images/avatar/64-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/avatar/64-1.jpg -------------------------------------------------------------------------------- /static/images/avatar/64-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/images/avatar/64-2.jpg -------------------------------------------------------------------------------- /static/fonts/icomoon/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/fonts/icomoon/icomoon.eot -------------------------------------------------------------------------------- /static/fonts/icomoon/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/fonts/icomoon/icomoon.ttf -------------------------------------------------------------------------------- /apps/libro/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class LibroConfig(AppConfig): 5 | name = 'libro' 6 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | 3 | DJANGO_SETTINGS_MODULE = biblioteca.settings 4 | 5 | python_files = tests.py test_*.py *_test.py -------------------------------------------------------------------------------- /static/fonts/icomoon/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/fonts/icomoon/icomoon.woff -------------------------------------------------------------------------------- /apps/usuario/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsuarioConfig(AppConfig): 5 | name = 'usuario' 6 | -------------------------------------------------------------------------------- /apps/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/apps/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apps/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/apps/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /static/css/lib/chosen/chosen-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/css/lib/chosen/chosen-sprite.png -------------------------------------------------------------------------------- /biblioteca/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/biblioteca/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /biblioteca/__pycache__/urls.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/biblioteca/__pycache__/urls.cpython-37.pyc -------------------------------------------------------------------------------- /biblioteca/__pycache__/wsgi.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/biblioteca/__pycache__/wsgi.cpython-36.pyc -------------------------------------------------------------------------------- /biblioteca/__pycache__/wsgi.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/biblioteca/__pycache__/wsgi.cpython-37.pyc -------------------------------------------------------------------------------- /static/css/lib/chosen/chosen-sprite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/static/css/lib/chosen/chosen-sprite@2x.png -------------------------------------------------------------------------------- /biblioteca/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/biblioteca/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /biblioteca/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/biblioteca/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /biblioteca/__pycache__/settings.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/biblioteca/__pycache__/settings.cpython-36.pyc -------------------------------------------------------------------------------- /biblioteca/__pycache__/settings.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/biblioteca/HEAD/biblioteca/__pycache__/settings.cpython-37.pyc -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.factories import UsuarioAdminFactory 4 | 5 | from apps.usuario.models import Usuario, Rol 6 | 7 | @pytest.fixture 8 | def user_creation(): 9 | return UsuarioAdminFactory.create() -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Documentación de Biblioteca 2 | 3 | nav: 4 | - Inicio: 'index.md' 5 | - 'Guía de Usuario': 6 | - 'Sistema de Autenticación': 'authenticated.md' 7 | - Acerca de: 'about.md' 8 | 9 | theme: readthedocs 10 | -------------------------------------------------------------------------------- /apps/usuario/templatetags/has_group.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | @register.filter(name = 'has_group') 6 | def has_group(usuario,grupo): 7 | return usuario.groups.filter(name__exact = grupo).exists() 8 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | DOCUMENTACIÓN DE BIBLIOTECA 2 | =========================== 3 | 4 | .. toctree:: 5 | :caption: Introduccion 6 | 7 | introduction 8 | 9 | .. toctree:: 10 | :caption: Guía de Usuario 11 | 12 | authenticated 13 | about 14 | -------------------------------------------------------------------------------- /tests/providers/general_providers.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from faker.providers import BaseProvider 3 | 4 | fake = Faker() 5 | 6 | class EmailProvider(BaseProvider): 7 | 8 | def custom_email(self): 9 | return f'{fake.last_name().lower()}@gmail.com' -------------------------------------------------------------------------------- /apps/usuario/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.auth.models import Permission 3 | from django.contrib.contenttypes.models import ContentType 4 | from apps.usuario.models import Usuario,Rol 5 | 6 | 7 | admin.site.register(Rol) 8 | admin.site.register(Usuario) 9 | admin.site.register(Permission) 10 | admin.site.register(ContentType) 11 | -------------------------------------------------------------------------------- /templates/libro/autor_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% block titulo %} 3 | Biblioteca.dev | confirmación de Eliminación 4 | {% endblock titulo %} 5 | 6 | {% block body %} 7 | 8 |
9 | {% csrf_token %} 10 |

¿Desea eliminar el registro {{ object }}?

11 | 12 |
13 | 14 | {% endblock body %} 15 | -------------------------------------------------------------------------------- /templates/libro/libro_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% block titulo %} 3 | Biblioteca.dev | confirmación de Eliminación 4 | {% endblock titulo %} 5 | 6 | {% block body %} 7 | 8 |
9 | {% csrf_token %} 10 |

¿Desea eliminar el registro {{ object }}?

11 | 12 |
13 | 14 | {% endblock body %} 15 | -------------------------------------------------------------------------------- /static/js/lib/data-table/buttons.bootstrap.min.js: -------------------------------------------------------------------------------- 1 | (function(b,a){b.extend(!0,a.Buttons.defaults,{dom:{container:{className:"dt-buttons btn-group"},button:{className:"btn btn-default"},collection:{tag:"ul",className:"dt-button-collection dropdown-menu",button:{tag:"li",className:"dt-button"},buttonLiner:{tag:"a",className:""}}}});a.ext.buttons.collection.text=function(a){return a.i18n("buttons.collection",'Collection ')}})(jQuery,jQuery.fn.dataTable); -------------------------------------------------------------------------------- /apps/libro/migrations/0003_auto_20210719_1251.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2021-07-19 17:51 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('libro', '0002_auto_20210718_1323'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | model_name='libro', 15 | old_name='autor_id', 16 | new_name='autor', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /biblioteca/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for biblioteca project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "biblioteca.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "biblioteca.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /apps/libro/formsets.py: -------------------------------------------------------------------------------- 1 | from django.forms import formset_factory 2 | from django.urls import reverse_lazy 3 | from django.views.generic.edit import FormView 4 | from apps.libro.models import Autor 5 | from apps.libro.forms import AutorForm 6 | 7 | class FormsetAutor(FormView): 8 | template_name = 'libro/autor/autor_formset.html' 9 | form_class = formset_factory(AutorForm,extra = 1) 10 | success_url = reverse_lazy('libro:inicio_autor') 11 | 12 | def form_valid(self,form): 13 | for f in form: 14 | if f.is_valid(): 15 | f.save() 16 | #print(f) 17 | return super().form_valid(form) -------------------------------------------------------------------------------- /tests/test_libro.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from faker import Faker 4 | from ddf import G, F 5 | 6 | from apps.libro.models import Autor, Libro 7 | 8 | fake = Faker() 9 | 10 | @pytest.fixture 11 | def create_libro(): 12 | # forma 1 13 | autor_1 = G(Autor, ) 14 | autor_2 = G(Autor) 15 | return G(Libro, autor=[autor_1, autor_2, F()]) 16 | 17 | # forma 2 18 | # autores = [F(nombre=fake.last_name()), F(nombre=fake.first_name())] 19 | 20 | # forma 3 21 | # return G(Libro, autor=[F(nombre=fake.last_name()), F(nombre=fake.first_name())]) 22 | 23 | @pytest.mark.django_db 24 | def test_create_libro(create_libro): 25 | print(create_libro.autor.all()) 26 | assert create_libro.estado 27 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | atomicwrites==1.4.0 2 | attrs==21.2.0 3 | colorama==0.4.4 4 | defusedxml==0.7.1 5 | diff-match-patch==20200713 6 | Django==2.2.13 7 | django-dynamic-fixture==3.1.1 8 | django-import-export==2.5.0 9 | django-querycount==0.7.0 10 | et-xmlfile==1.1.0 11 | factory-boy==3.2.0 12 | Faker==8.10.1 13 | iniconfig==1.1.1 14 | MarkupPy==1.14 15 | model-bakery==1.3.2 16 | odfpy==1.4.1 17 | openpyxl==3.0.7 18 | packaging==21.0 19 | Pillow==8.3.1 20 | pluggy==0.13.1 21 | py==1.10.0 22 | pyparsing==2.4.7 23 | pytest==6.2.4 24 | pytest-django==4.4.0 25 | python-dateutil==2.8.2 26 | pytz==2020.1 27 | PyYAML==5.4.1 28 | six==1.16.0 29 | sqlparse==0.3.1 30 | tablib==3.0.0 31 | text-unidecode==1.3 32 | toml==0.10.2 33 | xlrd==2.0.1 34 | xlwt==1.3.0 35 | -------------------------------------------------------------------------------- /templates/libro/autor/eliminar_autor.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/libro/libro/eliminar_libro.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/usuarios/eliminar_usuario.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /biblioteca/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.conf import settings 3 | from django.views.static import serve 4 | from django.urls import path,include,re_path 5 | from django.contrib.auth.decorators import login_required 6 | from apps.usuario.views import Inicio,Login,logoutUsuario 7 | 8 | urlpatterns = [ 9 | path('admin/', admin.site.urls), 10 | path('usuarios/',include(('apps.usuario.urls','usuarios'))), 11 | path('libro/',include(('apps.libro.urls','libro'))), 12 | path('',Inicio.as_view(), name = 'index'), 13 | path('accounts/login/',Login.as_view(), name = 'login'), 14 | path('logout/',login_required(logoutUsuario),name = 'logout'), 15 | ] 16 | 17 | urlpatterns += [ 18 | re_path(r'^media/(?P.*)$', serve, { 19 | 'document_root': settings.MEDIA_ROOT, 20 | }) 21 | ] -------------------------------------------------------------------------------- /apps/usuario/middleware.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from datetime import timedelta 3 | from apps.libro.models import Reserva 4 | 5 | class PruebaMiddleware: 6 | 7 | def __init__(self,get_response): 8 | self.get_response = get_response 9 | 10 | def __call__(self,request): 11 | response = self.get_response(request) 12 | return response 13 | 14 | def process_view(self,request,view_func,view_args,view_kwargs): 15 | if request.user.is_authenticated: 16 | fecha_actual = datetime.date.today() 17 | reservas = Reserva.objects.filter(estado = True,usuario = request.user) 18 | for reserva in reservas: 19 | fecha_vencimiento = reserva.fecha_creacion + timedelta(days = 7) 20 | if fecha_actual > fecha_vencimiento: 21 | reserva.estado = False 22 | reserva.save() -------------------------------------------------------------------------------- /apps/libro/migrations/0002_auto_20210718_1323.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2021-07-18 18:23 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('libro', '0001_initial'), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name='reserva', 20 | name='usuario', 21 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 22 | ), 23 | migrations.AddField( 24 | model_name='libro', 25 | name='autor_id', 26 | field=models.ManyToManyField(to='libro.Autor'), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /apps/usuario/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from django.contrib.auth.decorators import login_required 3 | from django.views.generic import TemplateView 4 | from apps.usuario.views import ( 5 | InicioUsuarios, ListadoUsuario, RegistrarUsuario, 6 | EditarUsuario, EliminarUsuario, CambiarPassword 7 | ) 8 | 9 | urlpatterns = [ 10 | path('inicio_usuarios/', InicioUsuarios.as_view(), name='inicio_usuarios'), 11 | path('listado_usuarios/', ListadoUsuario.as_view(),{'parametro_extra': 'Hola!'},name='listar_usuarios'), 12 | path('registrar_usuario/',RegistrarUsuario.as_view(),name = 'registrar_usuario'), 13 | path('actualizar_usuario//',EditarUsuario.as_view(), name = 'actualizar_usuario'), 14 | path('eliminar_usuario//',EliminarUsuario.as_view(), name='eliminar_usuario'), 15 | path('cambiar_password/',CambiarPassword.as_view(), name='cambiar_password'), 16 | ] 17 | -------------------------------------------------------------------------------- /templates/libro/libro/crear_libro.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/authenticated.md: -------------------------------------------------------------------------------- 1 | # Titulo principal 2 | 3 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit iusto sint numquam, cumque tenetur illum quos eius laudantium itaque autem quo enim error repellendus deserunt sunt esse modi consequuntur quas. 4 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit iusto sint numquam, cumque tenetur illum quos eius laudantium itaque autem quo enim error repellendus deserunt sunt esse modi consequuntur quas. 5 | 6 | ## Titulo secundario 1 7 | 8 | ### Subtitulo secundario 9 | 10 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit iusto sint numquam, cumque tenetur illum quos eius laudantium itaque autem quo enim error repellendus deserunt sunt esse modi consequuntur quas. 11 | 12 | ## Titulo secundario 2 13 | 14 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit iusto sint numquam, cumque tenetur illum quos eius laudantium itaque autem quo enim error repellendus deserunt sunt esse modi consequuntur quas. -------------------------------------------------------------------------------- /static/css/lib/vector-map/jqvmap.min.css: -------------------------------------------------------------------------------- 1 | .jqvmap-label, 2 | .jqvmap-pin { 3 | pointer-events: none 4 | } 5 | .jqvmap-label { 6 | position: absolute; 7 | display: none; 8 | -webkit-border-radius: 3px; 9 | -moz-border-radius: 3px; 10 | border-radius: 3px; 11 | background: #292929; 12 | color: #fff; 13 | font-family: sans-serif, Verdana; 14 | font-size: smaller; 15 | padding: 3px 16 | } 17 | .jqvmap-zoomin, 18 | .jqvmap-zoomout { 19 | position: absolute; 20 | left: 10px; 21 | -webkit-border-radius: 3px; 22 | -moz-border-radius: 3px; 23 | border-radius: 3px; 24 | background: #000; 25 | padding: 3px; 26 | color: #fff; 27 | width: 15px; 28 | height: 15px; 29 | cursor: pointer; 30 | line-height: 10px; 31 | text-align: center 32 | } 33 | .jqvmap-zoomin { 34 | top: 10px 35 | } 36 | .jqvmap-zoomout { 37 | top: 30px 38 | } 39 | .jqvmap-region { 40 | cursor: pointer 41 | } 42 | .jqvmap-ajax_response { 43 | width: 100%; 44 | height: 500px 45 | } -------------------------------------------------------------------------------- /templates/usuarios/crear_usuario.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/factories.py: -------------------------------------------------------------------------------- 1 | import factory 2 | from faker import Faker 3 | 4 | from tests.providers.general_providers import EmailProvider 5 | from apps.usuario.models import Usuario, Rol 6 | 7 | fake = Faker() 8 | fake.add_provider(EmailProvider) 9 | 10 | class RolFactory(factory.Factory): 11 | class Meta: 12 | model = Rol 13 | 14 | rol = 'admin' 15 | 16 | class UsuarioComunFactory(factory.Factory): 17 | class Meta: 18 | model = Usuario 19 | 20 | nombres = "Oliver" 21 | username = "oliver" 22 | email = fake.email() 23 | is_staff = False 24 | 25 | class UsuarioAdminFactory(factory.Factory): 26 | class Meta: 27 | model = Usuario 28 | 29 | nombres = "Oliver" 30 | username = "oliver" 31 | is_staff = True 32 | is_superuser = True 33 | #rol = factory.SubFactory(RolFactory) 34 | 35 | class UsuarioStaffFactory(factory.django.DjangoModelFactory): 36 | class Meta: 37 | model = Usuario 38 | 39 | nombres = "Oliver" 40 | username = "oliver" 41 | email = fake.email() 42 | is_staff = True -------------------------------------------------------------------------------- /templates/libro/autor/autor.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/libro/autor/crear_autor.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/usuarios/editar_usuario.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/libro/libro/libro.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/authenticated.rst: -------------------------------------------------------------------------------- 1 | Titulo principal 2 | ================ 3 | 4 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit 5 | iusto sint numquam, cumque tenetur illum quos eius laudantium itaque 6 | autem quo enim error repellendus deserunt sunt esse modi consequuntur 7 | quas. Lorem ipsum dolor sit amet, consectetur adipisicing elit. 8 | Reprehenderit iusto sint numquam, cumque tenetur illum quos eius 9 | laudantium itaque autem quo enim error repellendus deserunt sunt esse 10 | modi consequuntur quas. 11 | 12 | Titulo secundario 1 13 | ------------------- 14 | 15 | Subtitulo secundario 16 | ~~~~~~~~~~~~~~~~~~~~ 17 | 18 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit 19 | iusto sint numquam, cumque tenetur illum quos eius laudantium itaque 20 | autem quo enim error repellendus deserunt sunt esse modi consequuntur 21 | quas. 22 | 23 | Titulo secundario 2 24 | ------------------- 25 | 26 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit 27 | iusto sint numquam, cumque tenetur illum quos eius laudantium itaque 28 | autem quo enim error repellendus deserunt sunt esse modi consequuntur 29 | quas. 30 | -------------------------------------------------------------------------------- /docs/about.md: -------------------------------------------------------------------------------- 1 | # Titulo principal 2 | 3 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit iusto sint numquam, cumque tenetur illum quos eius laudantium itaque autem quo enim error repellendus deserunt sunt esse modi consequuntur quas. 4 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit iusto sint numquam, cumque tenetur illum quos eius laudantium itaque autem quo enim error repellendus deserunt sunt esse modi consequuntur quas. 5 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit iusto sint numquam, cumque tenetur illum quos eius laudantium itaque autem quo enim error repellendus deserunt sunt esse modi consequuntur quas. 6 | 7 | ## Titulo secundario 1 8 | 9 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit iusto sint numquam, cumque tenetur illum quos eius laudantium itaque autem quo enim error repellendus deserunt sunt esse modi consequuntur quas. 10 | 11 | ## Titulo secundario 2 12 | 13 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit iusto sint numquam, cumque tenetur illum quos eius laudantium itaque autem quo enim error repellendus deserunt sunt esse modi consequuntur quas. -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # DOCUMENTACIÓN DE BIBLIOTECA 2 | 3 | Esta es una documentación de prueba creada dentro del Curso de Django utilizando la librería 4 | MKDOCS para publicarlo en **ReadTheDocs** 5 | 6 | ## Requerimientos 7 | 8 | - Clona el siguiente repositorio: [Repositorio de Biblioteca](https://github.com/developerpe/biblioteca). 9 | 10 | ## Login 11 | 12 | ```python 13 | class Login(FormView): 14 | template_name = 'login.html' 15 | form_class = FormularioLogin 16 | success_url = reverse_lazy('index') 17 | 18 | @method_decorator(csrf_protect) 19 | @method_decorator(never_cache) 20 | def dispatch(self, request, *args, **kwargs): 21 | if request.user.is_authenticated: 22 | return HttpResponseRedirect(self.get_success_url()) 23 | else: 24 | return super(Login, self).dispatch(request, *args, **kwargs) 25 | 26 | def form_valid(self, form): 27 | login(self.request, form.get_user()) 28 | return super(Login, self).form_valid(form) 29 | ``` 30 | 31 | ## Project layout 32 | 33 | mkdocs.yml # The configuration file. 34 | docs/ 35 | index.md # The documentation homepage. 36 | ... # Other markdown pages, images and other files. 37 | -------------------------------------------------------------------------------- /docs/about.rst: -------------------------------------------------------------------------------- 1 | Titulo principal 2 | ================ 3 | 4 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit 5 | iusto sint numquam, cumque tenetur illum quos eius laudantium itaque 6 | autem quo enim error repellendus deserunt sunt esse modi consequuntur 7 | quas. Lorem ipsum dolor sit amet, consectetur adipisicing elit. 8 | Reprehenderit iusto sint numquam, cumque tenetur illum quos eius 9 | laudantium itaque autem quo enim error repellendus deserunt sunt esse 10 | modi consequuntur quas. Lorem ipsum dolor sit amet, consectetur 11 | adipisicing elit. Reprehenderit iusto sint numquam, cumque tenetur illum 12 | quos eius laudantium itaque autem quo enim error repellendus deserunt 13 | sunt esse modi consequuntur quas. 14 | 15 | Titulo secundario 1 16 | ------------------- 17 | 18 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit 19 | iusto sint numquam, cumque tenetur illum quos eius laudantium itaque 20 | autem quo enim error repellendus deserunt sunt esse modi consequuntur 21 | quas. 22 | 23 | Titulo secundario 2 24 | ------------------- 25 | 26 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit 27 | iusto sint numquam, cumque tenetur illum quos eius laudantium itaque 28 | autem quo enim error repellendus deserunt sunt esse modi consequuntur 29 | quas. 30 | -------------------------------------------------------------------------------- /static/js/init/datatables-init.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | // "use strict"; 3 | 4 | 5 | /* Data Table 6 | -------------*/ 7 | 8 | 9 | 10 | 11 | $('#bootstrap-data-table').DataTable({ 12 | lengthMenu: [[10, 20, 50, -1], [10, 20, 50, "All"]], 13 | }); 14 | 15 | 16 | 17 | $('#bootstrap-data-table-export').DataTable({ 18 | dom: 'lBfrtip', 19 | lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "All"]], 20 | buttons: [ 21 | 'copy', 'csv', 'excel', 'pdf', 'print' 22 | ] 23 | }); 24 | 25 | $('#row-select').DataTable( { 26 | initComplete: function () { 27 | this.api().columns().every( function () { 28 | var column = this; 29 | var select = $('') 30 | .appendTo( $(column.footer()).empty() ) 31 | .on( 'change', function () { 32 | var val = $.fn.dataTable.util.escapeRegex( 33 | $(this).val() 34 | ); 35 | 36 | column 37 | .search( val ? '^'+val+'$' : '', true, false ) 38 | .draw(); 39 | } ); 40 | 41 | column.data().unique().sort().each( function ( d, j ) { 42 | select.append( '' ) 43 | } ); 44 | } ); 45 | } 46 | } ); 47 | 48 | 49 | 50 | 51 | 52 | 53 | })(jQuery); -------------------------------------------------------------------------------- /static/js/widgets.js: -------------------------------------------------------------------------------- 1 | ( function ( $ ) { 2 | "use strict"; 3 | 4 | 5 | 6 | $.plot("#flotBar1", [{ 7 | data: [[0, 3], [2, 8], [4, 5], [6, 13],[8,5], [10,7],[12,4], [14,6]], 8 | bars: { 9 | show: true, 10 | lineWidth: 0, 11 | fillColor: '#85c988' 12 | } 13 | }], { 14 | grid: { 15 | show: false, 16 | hoverable: true 17 | } 18 | }); 19 | 20 | 21 | 22 | var plot = $.plot($('#flotLine1'),[{ 23 | data: [[0, 1], [1, 3], [2,6], [3, 5], [4, 7], [5, 8], [6, 10]], 24 | color: '#fff' 25 | }], 26 | { 27 | series: { 28 | lines: { 29 | show: false 30 | }, 31 | splines: { 32 | show: true, 33 | tension: 0.4, 34 | lineWidth: 2 35 | //fill: 0.4 36 | }, 37 | shadowSize: 0 38 | }, 39 | points: { 40 | show: false, 41 | }, 42 | legend: { 43 | noColumns: 1, 44 | position: 'nw' 45 | }, 46 | grid: { 47 | hoverable: true, 48 | clickable: true, 49 | show: false 50 | }, 51 | yaxis: { 52 | min: 0, 53 | max: 10, 54 | color: '#eee', 55 | font: { 56 | size: 10, 57 | color: '#6a7074' 58 | } 59 | }, 60 | xaxis: { 61 | color: '#eee', 62 | font: { 63 | size: 10, 64 | color: '#6a7074' 65 | } 66 | } 67 | }); 68 | 69 | } )( jQuery ); -------------------------------------------------------------------------------- /apps/libro/views_function.py: -------------------------------------------------------------------------------- 1 | 2 | def crearAutor(request): 3 | if request.method == 'POST': 4 | autor_form = AutorForm(request.POST) 5 | if autor_form.is_valid(): 6 | autor_form.save() 7 | return redirect('index') 8 | else: 9 | autor_form = AutorForm() 10 | return render(request,'libro/crear_autor.html',{'autor_form':autor_form}) 11 | 12 | 13 | def listarAutor(request): 14 | autores = Autor.objects.filter(estado = True) 15 | return render(request,'libro/listar_autor.html',{'autores':autores}) 16 | 17 | def editarAutor(request,id): 18 | autor_form = None 19 | error = None 20 | try: 21 | autor = Autor.objects.get(id = id) 22 | if request.method == 'GET': 23 | autor_form = AutorForm(instance = autor) 24 | else: 25 | autor_form = AutorForm(request.POST, instance = autor) 26 | if autor_form.is_valid(): 27 | autor_form.save() 28 | return redirect('libro:listar_autor') 29 | except ObjectDoesNotExist as e: 30 | error = e 31 | return render(request,'libro/crear_autor.html',{'autor_form':autor_form,'error':error}) 32 | 33 | def eliminarAutor(request,id): 34 | autor = Autor.objects.get(id = id) 35 | autor.estado = False 36 | autor.save() 37 | return redirect('libro:listar_autor') 38 | -------------------------------------------------------------------------------- /templates/libro/reservas_vencidas.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% load staticfiles %} 3 | {% block titulo %} 4 | Biblioteca.dev | Listado de Reservas Vencidas 5 | {% endblock titulo %} 6 | {% block extra_css %} 7 | 8 | {% endblock extra_css %} 9 | {% block body %} 10 |
11 |
12 |
13 |
14 | Listado de Reservas Vencidas 15 |
16 |
17 | 18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
#TítuloFecha de Reserva
31 |
32 |
33 | 34 | 35 | {% endblock body %} 36 | {% block extrajs %} 37 | 38 | 39 | 40 | {% endblock extrajs %} -------------------------------------------------------------------------------- /docs/introduction.rst: -------------------------------------------------------------------------------- 1 | DOCUMENTACIÓN DE BIBLIOTECA 2 | =========================== 3 | 4 | Esta es una documentación de prueba creada dentro del Curso de Django 5 | utilizando la librería MKDOCS para publicarlo en **ReadTheDocs** 6 | 7 | Requerimientos 8 | -------------- 9 | 10 | - Clona el siguiente repositorio: `Repositorio de 11 | Biblioteca `__. 12 | 13 | Login 14 | ----- 15 | 16 | .. code:: python 17 | 18 | class Login(FormView): 19 | template_name = 'login.html' 20 | form_class = FormularioLogin 21 | success_url = reverse_lazy('index') 22 | 23 | @method_decorator(csrf_protect) 24 | @method_decorator(never_cache) 25 | def dispatch(self, request, *args, **kwargs): 26 | if request.user.is_authenticated: 27 | return HttpResponseRedirect(self.get_success_url()) 28 | else: 29 | return super(Login, self).dispatch(request, *args, **kwargs) 30 | 31 | def form_valid(self, form): 32 | login(self.request, form.get_user()) 33 | return super(Login, self).form_valid(form) 34 | 35 | Project layout 36 | -------------- 37 | 38 | :: 39 | 40 | mkdocs.yml # The configuration file. 41 | docs/ 42 | index.md # The documentation homepage. 43 | ... # Other markdown pages, images and other files. 44 | 45 | -------------------------------------------------------------------------------- /apps/usuario/mixins.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import redirect 2 | from django.contrib import messages 3 | from django.urls import reverse_lazy 4 | 5 | class LoginYSuperStaffMixin(object): 6 | 7 | def dispatch(self, request, *args, **kwargs): 8 | if request.user.is_authenticated: 9 | if request.user.is_staff: 10 | return super().dispatch(request, *args, **kwargs) 11 | return redirect('index') 12 | 13 | class LoginMixin(object): 14 | 15 | def dispatch(self, request, *args, **kwargs): 16 | if request.user.is_authenticated: 17 | return super().dispatch(request, *args, **kwargs) 18 | return redirect('index') 19 | 20 | class ValidarPermisosMixin(object): 21 | permission_required = '' 22 | url_redirect = None 23 | 24 | def get_perms(self): 25 | if isinstance(self.permission_required,str): return (self.permission_required) 26 | else: return self.permission_required 27 | 28 | def get_url_redirect(self): 29 | if self.url_redirect is None: 30 | return reverse_lazy('login') 31 | return self.url_redirect 32 | 33 | def dispatch(self, request, *args, **kwargs): 34 | if request.user.has_perms(self.get_perms()): 35 | return super().dispatch(request, *args, **kwargs) 36 | messages.error(request, 'No tienes permisos para realizar esta acción.') 37 | return redirect(self.get_url_redirect()) 38 | 39 | -------------------------------------------------------------------------------- /apps/libro/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from import_export import resources 3 | from import_export.admin import ImportExportModelAdmin 4 | from .models import Autor,Libro,Reserva 5 | from .forms import ReservaForm 6 | 7 | class ReservaAdmin(admin.ModelAdmin): 8 | form = ReservaForm 9 | list_display = ('libro','usuario','fecha_creacion','fecha_vencimiento','estado') 10 | 11 | class AutorResource(resources.ModelResource): 12 | class Meta: 13 | model = Autor 14 | 15 | class AutorAdmin(ImportExportModelAdmin,admin.ModelAdmin): 16 | search_fields = ('nombre','apellidos','nacionalidad') 17 | list_display = ('nombre','apellidos','nacionalidad','estado') 18 | resource_class = AutorResource 19 | actions = ['eliminacion_logica_autores', 'activacion_logica_autores'] 20 | 21 | def eliminacion_logica_autores(self, request, queryset): 22 | for autor in queryset: 23 | autor.estado = False 24 | autor.save() 25 | 26 | def activacion_logica_autores(self, request, queryset): 27 | for autor in queryset: 28 | autor.estado = True 29 | autor.save() 30 | """ 31 | def get_actions(self,request): 32 | actions = super().get_actions(request) 33 | if 'delete_selected' in actions: 34 | del actions['delete_selected'] 35 | return actions 36 | """ 37 | 38 | admin.site.register(Autor, AutorAdmin) 39 | admin.site.register(Libro) 40 | admin.site.register(Reserva,ReservaAdmin) 41 | -------------------------------------------------------------------------------- /convenciones_comentarios_codigo.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | PEP 257 4 | 5 | 6 | def get_queryset(self): 7 | """Retorna la consulta a utilizar en toda la clase""" 8 | return self.model.objects.filter(estado=True) 9 | 10 | def get_queryset(self,request): 11 | """Retorna una consulta a utilizarse en la clase 12 | 13 | 14 | Argumentos: 15 | request: la peticion enviada del navegador 16 | 17 | """ 18 | return self.model.objects.filter(estado=True) 19 | 20 | 21 | SPHINX 22 | 23 | def get_queryset(self,request): 24 | """Retorna una consulta a utilizarse en la clase. 25 | Esta funcion se encuentra en toda vista basada en clase, se utiliza internamente por django para 26 | generar las consultas de a cuerdo a los valores que se definen en la clase, valores como MODEL,FORM_CLASS 27 | 28 | 29 | :parámetro request: petición enviada del navegador 30 | :type request: dict 31 | :return: una consulta 32 | :rtype: Queryset 33 | """ 34 | 35 | 36 | return self.model.objects.filter(estado=True) 37 | 38 | GOOGLE PYTHON 39 | 40 | def get_queryset(self,request): 41 | """Retorna una consulta a utilizarse en la clase. 42 | Esta funcion se encuentra en toda vista basada en clase, se utiliza internamente por django para 43 | generar las consultas de a cuerdo a los valores que se definen en la clase, valores como MODEL, FORM_CLASS 44 | 45 | Argumentos: 46 | request: peticion enviada del navegador 47 | 48 | 49 | Returns: 50 | Una consulta 51 | """ 52 | 53 | 54 | return self.model.objects.filter(estado=True) 55 | 56 | -------------------------------------------------------------------------------- /templates/libro/libro/listar_libro.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% load staticfiles %} 3 | {% block titulo %} 4 | Biblioteca.dev | Listado de Libros 5 | {% endblock titulo %} 6 | {% block extra_css %} 7 | 8 | {% endblock extra_css %} 9 | {% block body %} 10 |
11 |
12 |
13 |
14 | Listado de Libros 15 |
16 |
17 | 19 |
20 |
21 | 22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
#TítuloFecha de PublicaciónAutorOpciones
37 |
38 |
39 | 42 | 45 | 48 | 49 | 50 | {% endblock body %} 51 | {% block extrajs %} 52 | 53 | 54 | 55 | {% endblock extrajs %} -------------------------------------------------------------------------------- /templates/libro/autor/listar_autor.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% load staticfiles %} 3 | {% block titulo %} 4 | Biblioteca.dev | Listado de Autores 5 | {% endblock titulo %} 6 | {% block extra_css %} 7 | 8 | {% endblock extra_css %} 9 | {% block body %} 10 |
11 |
12 |
13 |
14 | Listado de Autores 15 |
16 |
17 | 19 |
20 |
21 | 22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
#NombresApellidosNacionalidadDescripcion
37 |
38 |
39 | 42 | 45 | 48 | 49 | 50 | {% endblock body %} 51 | {% block extrajs %} 52 | 53 | 54 | 55 | {% endblock extrajs %} -------------------------------------------------------------------------------- /static/js/lib/data-table/buttons.print.min.js: -------------------------------------------------------------------------------- 1 | (function(e){"function"===typeof define&&define.amd?define(["jquery","datatables.net","datatables.net-buttons"],function(f){return e(f,window,document)}):"object"===typeof exports?module.exports=function(f,c){f||(f=window);if(!c||!c.fn.dataTable)c=require("datatables.net")(f,c).$;c.fn.dataTable.Buttons||require("datatables.net-buttons")(f,c);return e(c,f,f.document)}:e(jQuery,window,document)})(function(e,f,c){var i=e.fn.dataTable,h=c.createElement("a");i.ext.buttons.print={className:"buttons-print", 2 | text:function(b){return b.i18n("buttons.print","Print")},action:function(b,c,i,d){var a=c.buttons.exportData(d.exportOptions),k=function(b,a){for(var c="",d=0,e=b.length;d"+b[d]+"";return c+""},b='';d.header&&(b+=""+k(a.header,"th")+"");for(var b=b+"",l=0,m=a.body.length;l";d.footer&&a.footer&&(b+=""+k(a.footer,"th")+"");var g=f.open("",""), 3 | a=d.title;"function"===typeof a&&(a=a());-1!==a.indexOf("*")&&(a=a.replace("*",e("title").text()));g.document.close();var j=""+a+"";e("style, link").each(function(){var c=j,b=e(this).clone()[0],a;"link"===b.nodeName.toLowerCase()&&(h.href=b.href,a=h.host,-1===a.indexOf("/")&&0!==h.pathname.indexOf("/")&&(a+="/"),b.href=h.protocol+"//"+a+h.pathname+h.search);j=c+b.outerHTML});try{g.document.head.innerHTML=j}catch(n){e(g.document.head).html(j)}g.document.body.innerHTML="

"+a+"

"+ 4 | ("function"===typeof d.message?d.message(c,i,d):d.message)+"
"+b;e(g.document.body).addClass("dt-print-view");d.customize&&d.customize(g);setTimeout(function(){d.autoPrint&&(g.print(),g.close())},250)},title:"*",message:"",exportOptions:{},header:!0,footer:!1,autoPrint:!0,customize:null};return i.Buttons}); 5 | -------------------------------------------------------------------------------- /apps/libro/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from django.contrib.auth.decorators import login_required 3 | from .views import * 4 | from .formsets import FormsetAutor 5 | 6 | urlpatterns = [ 7 | path('inicio_autor/',InicioAutor.as_view(), name = 'inicio_autor'), 8 | path('crear_autor/',CrearAutor.as_view(), name = 'crear_autor'), 9 | path('listado_autor/',ListadoAutor.as_view(), name = 'listar_autor'), 10 | path('editar_autor//',ActualizarAutor.as_view(), name = 'editar_autor'), 11 | path('eliminar_autor//',EliminarAutor.as_view(), name = 'eliminar_autor'), 12 | path('inicio_libro/',InicioLibro.as_view(), name = 'inicio_libro'), 13 | path('listado_libros/', ListadoLibros.as_view(), name = 'listado_libros'), 14 | path('crear_libro/',CrearLibro.as_view(), name = 'crear_libro'), 15 | path('editar_libro//', ActualizarLibro.as_view(), name = 'editar_libro'), 16 | path('eliminar_libro//', EliminarLibro.as_view(), name = 'eliminar_libro'), 17 | # URLS GENERALES 18 | path('reservas/',Reservas.as_view(), name = 'reservas'), 19 | path('reservas-vencidas/',ReservasVencidas.as_view(), name = 'reservas_vencidas'), 20 | path('listado-libros-disponibles/',ListadoLibrosDisponibles.as_view(), name = 'listado_libros_disponibles'), 21 | path('listado-libros-reservados/',ListadoLibrosReservados.as_view(), name = 'listado_libros_reservados'), 22 | path('listado-reservas-vencidas/',ListadoReservasVencias.as_view(), name = 'listado_reservas_vencidas'), 23 | path('detalle-libro//',DetalleLibroDiponible.as_view(), name = 'detalle_libro'), 24 | path('reservar-libro/',RegistrarReserva.as_view(), name = 'reservar_libro'), 25 | 26 | # FORMSETS 27 | path('crear_autor_formset', FormsetAutor.as_view(), name = 'crear_autor_formset') 28 | ] 29 | -------------------------------------------------------------------------------- /templates/usuarios/cambiar_password.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% load staticfiles %} 3 | {% block titulo %} 4 | Biblioteca.dev | Cambiar Contraseña 5 | {% endblock titulo %} 6 | {% block extra_css %} 7 | {% endblock extra_css %} 8 | {% block body %} 9 | 10 |
11 |

Formulario de Cambio de Contraseña

12 |
13 | 14 | 15 |
16 | {% csrf_token %} 17 |
18 |
19 | {% if form.password1.errors %} 20 | {% for error in form.password1.errors %} 21 |
22 | 25 | Error: {{ error | escape }} 26 |
27 | {% endfor %} 28 | {% endif %} 29 | {{ form.password1.label }} 30 | {{ form.password1 }} 31 |
32 | 33 |
34 | {% if form.password2.errors %} 35 | {% for error in form.password2.errors %} 36 |
37 | 40 | Error: {{ error | escape }} 41 |
42 | {% endfor %} 43 | {% endif %} 44 | {{ form.password2.label }} 45 | {{ form.password2 }} 46 |
47 | 48 |
49 | 50 | 51 | Cancelar 52 | 53 | {% endblock body %} -------------------------------------------------------------------------------- /templates/usuarios/listar_usuario.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% load staticfiles %} 3 | {% block titulo %} 4 | Biblioteca.dev | Listado de Usuarios 5 | {% endblock titulo %} 6 | {% block extra_css %} 7 | 8 | {% endblock extra_css %} 9 | {% block body %} 10 |
11 |
12 |
13 |
14 | Listado de Usuarios 15 |
16 |
17 | 18 |
19 |
20 | 21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
#UsernameNombresApellidosEmailOpciones
37 | 38 | 39 | 42 | 45 | 48 | {% endblock body %} 49 | {% block extrajs %} 50 | 51 | 52 | 53 | {% endblock extrajs %} -------------------------------------------------------------------------------- /static/js/lib/data-table/dataTables.bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | DataTables Bootstrap 3 integration 3 | ©2011-2015 SpryMedia Ltd - datatables.net/license 4 | */ 5 | (function(b){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(a){return b(a,window,document)}):"object"===typeof exports?module.exports=function(a,d){a||(a=window);if(!d||!d.fn.dataTable)d=require("datatables.net")(a,d).$;return b(d,a,a.document)}:b(jQuery,window,document)})(function(b,a,d,m){var f=b.fn.dataTable;b.extend(!0,f.defaults,{dom:"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", 6 | renderer:"bootstrap"});b.extend(f.ext.classes,{sWrapper:"dataTables_wrapper container-fluid dt-bootstrap4",sFilterInput:"form-control form-control-sm",sLengthSelect:"form-control form-control-sm",sProcessing:"dataTables_processing card",sPageButton:"paginate_button page-item"});f.ext.renderer.pageButton.bootstrap=function(a,h,r,s,j,n){var o=new f.Api(a),t=a.oClasses,k=a.oLanguage.oPaginate,u=a.oLanguage.oAria.paginate||{},e,g,p=0,q=function(d,f){var l,h,i,c,m=function(a){a.preventDefault();!b(a.currentTarget).hasClass("disabled")&& 7 | o.page()!=a.data.action&&o.page(a.data.action).draw("page")};l=0;for(h=f.length;l",{"class":t.sPageButton+" "+g,id:0===r&& 8 | "string"===typeof c?a.sTableId+"_"+c:null}).append(b("",{href:"#","aria-controls":a.sTableId,"aria-label":u[c],"data-dt-idx":p,tabindex:a.iTabIndex,"class":"page-link"}).html(e)).appendTo(d),a.oApi._fnBindAction(i,{action:c},m),p++)}},i;try{i=b(h).find(d.activeElement).data("dt-idx")}catch(v){}q(b(h).empty().html('
    ').children("ul"),s);i!==m&&b(h).find("[data-dt-idx="+i+"]").focus()};return f}); -------------------------------------------------------------------------------- /static/js/libro/reservas.js: -------------------------------------------------------------------------------- 1 | function listadoLibrosReservados() { 2 | $.ajax({ 3 | url: "/libro/reservas/", 4 | type: "get", 5 | dataType: "json", 6 | success: function (response) { 7 | if ($.fn.DataTable.isDataTable('#tabla_libros')) { 8 | $('#tabla_libros').DataTable().destroy(); 9 | } 10 | $('#tabla_libros tbody').html(""); 11 | for (let i = 0; i < response.length; i++) { 12 | let fila = ''; 13 | fila += '' + (i + 1) + ''; 14 | fila += '' + response[i]["fields"]['libro'] + ''; 15 | fila += '' + response[i]["fields"]['fecha_creacion'] + ''; 16 | fila += ''; 17 | $('#tabla_libros tbody').append(fila); 18 | } 19 | $('#tabla_libros').DataTable({ 20 | language: { 21 | "decimal": "", 22 | "emptyTable": "No hay información", 23 | "info": "Mostrando _START_ a _END_ de _TOTAL_ Entradas", 24 | "infoEmpty": "Mostrando 0 to 0 of 0 Entradas", 25 | "infoFiltered": "(Filtrado de _MAX_ total entradas)", 26 | "infoPostFix": "", 27 | "thousands": ",", 28 | "lengthMenu": "Mostrar _MENU_ Entradas", 29 | "loadingRecords": "Cargando...", 30 | "processing": "Procesando...", 31 | "search": "Buscar:", 32 | "zeroRecords": "Sin resultados encontrados", 33 | "paginate": { 34 | "first": "Primero", 35 | "last": "Ultimo", 36 | "next": "Siguiente", 37 | "previous": "Anterior" 38 | }, 39 | }, 40 | }); 41 | }, 42 | error: function (error) { 43 | console.log(error); 44 | } 45 | }); 46 | } 47 | $(document).ready(function () { 48 | listadoLibrosReservados(); 49 | }); -------------------------------------------------------------------------------- /static/js/libro/reservas_vencidas.js: -------------------------------------------------------------------------------- 1 | function listadoLibrosReservados() { 2 | $.ajax({ 3 | url: "/libro/reservas-vencidas/", 4 | type: "get", 5 | dataType: "json", 6 | success: function (response) { 7 | if ($.fn.DataTable.isDataTable('#tabla_libros')) { 8 | $('#tabla_libros').DataTable().destroy(); 9 | } 10 | $('#tabla_libros tbody').html(""); 11 | for (let i = 0; i < response.length; i++) { 12 | let fila = ''; 13 | fila += '' + (i + 1) + ''; 14 | fila += '' + response[i]["fields"]['libro'] + ''; 15 | fila += '' + response[i]["fields"]['fecha_creacion'] + ''; 16 | fila += ''; 17 | $('#tabla_libros tbody').append(fila); 18 | } 19 | $('#tabla_libros').DataTable({ 20 | language: { 21 | "decimal": "", 22 | "emptyTable": "No hay información", 23 | "info": "Mostrando _START_ a _END_ de _TOTAL_ Entradas", 24 | "infoEmpty": "Mostrando 0 to 0 of 0 Entradas", 25 | "infoFiltered": "(Filtrado de _MAX_ total entradas)", 26 | "infoPostFix": "", 27 | "thousands": ",", 28 | "lengthMenu": "Mostrar _MENU_ Entradas", 29 | "loadingRecords": "Cargando...", 30 | "processing": "Procesando...", 31 | "search": "Buscar:", 32 | "zeroRecords": "Sin resultados encontrados", 33 | "paginate": { 34 | "first": "Primero", 35 | "last": "Ultimo", 36 | "next": "Siguiente", 37 | "previous": "Anterior" 38 | }, 39 | }, 40 | }); 41 | }, 42 | error: function (error) { 43 | console.log(error); 44 | } 45 | }); 46 | } 47 | $(document).ready(function () { 48 | listadoLibrosReservados(); 49 | }); -------------------------------------------------------------------------------- /templates/libro/libros_reservados.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% load staticfiles %} 3 | {% block titulo %} 4 | Biblioteca.dev | Listado de Libros Reservados 5 | {% endblock titulo %} 6 | {% block extra_css %} 7 | 8 | 9 | {% endblock extra_css %} 10 | {% block body %} 11 |
    12 |
    13 |
    14 |
    15 |

    Libros Reservados

    16 |
    17 |
    18 | 19 |
    20 |
    21 |
    22 | 23 | 24 | {% endblock body %} 25 | {% block extrajs %} 26 | 27 | 28 | 29 | 30 | 31 | 53 | {% endblock extrajs %} -------------------------------------------------------------------------------- /population.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import django,random as rd 4 | from random import random 5 | 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE","biblioteca.settings") 7 | 8 | django.setup() 9 | 10 | from apps.libro.models import Autor 11 | 12 | vocals = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'] 13 | consonants = ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'x', 'y', 'z', 14 | 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'X', 'Y', 'Z'] 15 | 16 | def generate_string(length): 17 | if length <= 0: 18 | return False 19 | 20 | random_string = '' 21 | 22 | for i in range(length): 23 | decision = rd.choice(('vocals','consonants')) 24 | 25 | if random_string[-1:].lower() in vocals: 26 | decision = 'consonants' 27 | if random_string[-1:].lower() in consonants: 28 | decision = 'vocals' 29 | 30 | if decision == 'vocals': 31 | character = rd.choice(vocals) 32 | else: 33 | character = rd.choice(consonants) 34 | 35 | random_string += character 36 | 37 | return random_string 38 | 39 | def generate_number(): 40 | return int(random()*10+1) 41 | 42 | def generate_autor(count): 43 | for j in range(count): 44 | print(f'Generando Autor #{j} . . .') 45 | random_name = generate_string(generate_number()) 46 | random_last_name = generate_string(generate_number()) 47 | random_country = generate_string(generate_number()) 48 | random_description = generate_string(generate_number()) 49 | 50 | Autor.objects.create( 51 | nombre=random_name, 52 | apellidos=random_last_name, 53 | nacionalidad=random_country, 54 | descripcion=random_description 55 | ) 56 | 57 | 58 | if __name__ == "__main__": 59 | print("Inicio de creación de población") 60 | print("Por favor espere . . . ") 61 | start = time.strftime("%c") 62 | print(f'Fecha y hora de inicio: {start}') 63 | generate_autor(190000) 64 | end = time.strftime("%c") 65 | print(f'Fecha y hora de finalización: {end}') 66 | -------------------------------------------------------------------------------- /templates/libro/libros_disponibles.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% load staticfiles %} 3 | {% block titulo %} 4 | Biblioteca.dev | Listado de Libros Disponibles 5 | {% endblock titulo %} 6 | {% block extra_css %} 7 | {% endblock extra_css %} 8 | {% block body %} 9 |
    10 |
    11 |
    12 |
    13 | 14 |

    Listado de Libros Disponibles

    15 |
    16 |
    17 |
    18 | 19 |
    36 | 37 | 54 |
    55 |
    56 | 57 | 58 | {% endblock body %} 59 | {% block extrajs %} 60 | {% endblock extrajs %} -------------------------------------------------------------------------------- /static/js/vmap.sampledata.js: -------------------------------------------------------------------------------- 1 | var sample_data = {"af":"16.63","al":"11.58","dz":"158.97","ao":"85.81","ag":"1.1","ar":"351.02","am":"8.83","au":"1219.72","at":"366.26","az":"52.17","bs":"7.54","bh":"21.73","bd":"105.4","bb":"3.96","by":"52.89","be":"461.33","bz":"1.43","bj":"6.49","bt":"1.4","bo":"19.18","ba":"16.2","bw":"12.5","br":"2023.53","bn":"11.96","bg":"44.84","bf":"8.67","bi":"1.47","kh":"11.36","cm":"21.88","ca":"1563.66","cv":"1.57","cf":"2.11","td":"7.59","cl":"199.18","cn":"5745.13","co":"283.11","km":"0.56","cd":"12.6","cg":"11.88","cr":"35.02","ci":"22.38","hr":"59.92","cy":"22.75","cz":"195.23","dk":"304.56","dj":"1.14","dm":"0.38","do":"50.87","ec":"61.49","eg":"216.83","sv":"21.8","gq":"14.55","er":"2.25","ee":"19.22","et":"30.94","fj":"3.15","fi":"231.98","fr":"2555.44","ga":"12.56","gm":"1.04","ge":"11.23","de":"3305.9","gh":"18.06","gr":"305.01","gd":"0.65","gt":"40.77","gn":"4.34","gw":"0.83","gy":"2.2","ht":"6.5","hn":"15.34","hk":"226.49","hu":"132.28","is":"12.77","in":"1430.02","id":"695.06","ir":"337.9","iq":"84.14","ie":"204.14","il":"201.25","it":"2036.69","jm":"13.74","jp":"5390.9","jo":"27.13","kz":"129.76","ke":"32.42","ki":"0.15","kr":"986.26","undefined":"5.73","kw":"117.32","kg":"4.44","la":"6.34","lv":"23.39","lb":"39.15","ls":"1.8","lr":"0.98","ly":"77.91","lt":"35.73","lu":"52.43","mk":"9.58","mg":"8.33","mw":"5.04","my":"218.95","mv":"1.43","ml":"9.08","mt":"7.8","mr":"3.49","mu":"9.43","mx":"1004.04","md":"5.36","mn":"5.81","me":"3.88","ma":"91.7","mz":"10.21","mm":"35.65","na":"11.45","np":"15.11","nl":"770.31","nz":"138","ni":"6.38","ne":"5.6","ng":"206.66","no":"413.51","om":"53.78","pk":"174.79","pa":"27.2","pg":"8.81","py":"17.17","pe":"153.55","ph":"189.06","pl":"438.88","pt":"223.7","qa":"126.52","ro":"158.39","ru":"1476.91","rw":"5.69","ws":"0.55","st":"0.19","sa":"434.44","sn":"12.66","rs":"38.92","sc":"0.92","sl":"1.9","sg":"217.38","sk":"86.26","si":"46.44","sb":"0.67","za":"354.41","es":"1374.78","lk":"48.24","kn":"0.56","lc":"1","vc":"0.58","sd":"65.93","sr":"3.3","sz":"3.17","se":"444.59","ch":"522.44","sy":"59.63","tw":"426.98","tj":"5.58","tz":"22.43","th":"312.61","tl":"0.62","tg":"3.07","to":"0.3","tt":"21.2","tn":"43.86","tr":"729.05","tm":0,"ug":"17.12","ua":"136.56","ae":"239.65","gb":"2258.57","us":"14624.18","uy":"40.71","uz":"37.72","vu":"0.72","ve":"285.21","vn":"101.99","ye":"30.02","zm":"15.69","zw":"5.57"}; -------------------------------------------------------------------------------- /tests/test_user.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from django.test import TestCase, Client 4 | 5 | from apps.usuario.models import Usuario 6 | from tests.factories import UsuarioAdminFactory, UsuarioComunFactory 7 | 8 | """ 9 | @pytest.mark.django_db 10 | def test_common_user_creation(user_creation): 11 | print(user_creation.rol) 12 | assert True 13 | 14 | @pytest.mark.django_db 15 | def test_superuser_creation(user_creation): 16 | user_creation.is_superuser = True 17 | user_creation.is_staff = True 18 | assert user_creation.is_superuser 19 | 20 | @pytest.mark.django_db 21 | def test_staff_user_creation(user_creation): 22 | user_creation.is_staff = True 23 | assert user_creation.is_staff 24 | 25 | @pytest.mark.django_db 26 | def test_user_creation_fail(): 27 | with pytest.raises(Exception): 28 | Usuario.objects.create_user( 29 | password='12345', 30 | is_staff=False 31 | ) 32 | """ 33 | 34 | class UsuarioTestCase(TestCase): 35 | 36 | def setUp(self): 37 | self.client = Client() 38 | self.common_user = UsuarioComunFactory.create() 39 | self.superuser = UsuarioAdminFactory.create() 40 | 41 | def test_common_user_creation(self): 42 | self.assertEqual(self.common_user.is_active, True) 43 | self.assertEqual(self.common_user.is_staff, False) 44 | self.assertEqual(self.common_user.is_superuser, False) 45 | 46 | def test_suerpuser_creation(self): 47 | self.assertEqual(self.superuser.is_staff, True) 48 | self.assertEqual(self.superuser.is_superuser, True) 49 | 50 | def test_login(self): 51 | self.common_user.set_password('oliver') 52 | self.common_user.save() 53 | response = self.client.login(username='oliver', password='oliver') 54 | self.assertEquals(response, True) 55 | 56 | def test_login_fail(self): 57 | self.common_user.set_password('oliver') 58 | self.common_user.save() 59 | response = self.client.login(username='oliver', password='oliver1') 60 | self.assertEquals(response, False) 61 | 62 | def test_users_list(self): 63 | self.superuser.set_password('oliver') 64 | self.superuser.save() 65 | self.client.login(username='oliver', password='oliver') 66 | response = self.client.get('/usuarios/listado_usuarios/', 67 | HTTP_X_REQUESTED_WITH='XMLHttpRequest') 68 | self.assertEquals(response.status_code, 200) 69 | self.assertEquals(len(response.json()), 1) -------------------------------------------------------------------------------- /static/js/lib/data-table/buttons.colVis.min.js: -------------------------------------------------------------------------------- 1 | (function(g){"function"===typeof define&&define.amd?define(["jquery","datatables.net","datatables.net-buttons"],function(d){return g(d,window,document)}):"object"===typeof exports?module.exports=function(d,e){d||(d=window);if(!e||!e.fn.dataTable)e=require("datatables.net")(d,e).$;e.fn.dataTable.Buttons||require("datatables.net-buttons")(d,e);return g(e,d,d.document)}:g(jQuery,window,document)})(function(g,d,e,h){d=g.fn.dataTable;g.extend(d.ext.buttons,{colvis:function(a,b){return{extend:"collection", 2 | text:function(a){return a.i18n("buttons.colvis","Column visibility")},className:"buttons-colvis",buttons:[{extend:"columnsToggle",columns:b.columns}]}},columnsToggle:function(a,b){return a.columns(b.columns).indexes().map(function(a){return{extend:"columnToggle",columns:a}}).toArray()},columnToggle:function(a,b){return{extend:"columnVisibility",columns:b.columns}},columnsVisibility:function(a,b){return a.columns(b.columns).indexes().map(function(a){return{extend:"columnVisibility",columns:a,visibility:b.visibility}}).toArray()}, 3 | columnVisibility:{columns:h,text:function(a,b,c){return c._columnText(a,c.columns)},className:"buttons-columnVisibility",action:function(a,b,c,f){a=b.columns(f.columns);b=a.visible();a.visible(f.visibility!==h?f.visibility:!(b.length&&b[0]))},init:function(a,b,c){var f=this;a.on("column-visibility.dt"+c.namespace,function(b,d){d.bDestroying||f.active(a.column(c.columns).visible())}).on("column-reorder.dt"+c.namespace,function(b,d,e){1===a.columns(c.columns).count()&&("number"===typeof c.columns&& 4 | (c.columns=e.mapping[c.columns]),b=a.column(c.columns),f.text(c._columnText(a,c.columns)),f.active(b.visible()))});this.active(a.column(c.columns).visible())},destroy:function(a,b,c){a.off("column-visibility.dt"+c.namespace).off("column-reorder.dt"+c.namespace)},_columnText:function(a,b){var c=a.column(b).index();return a.settings()[0].aoColumns[c].sTitle.replace(/\n/g," ").replace(/<.*?>/g,"").replace(/^\s+|\s+$/g,"")}},colvisRestore:{className:"buttons-colvisRestore",text:function(a){return a.i18n("buttons.colvisRestore", 5 | "Restore visibility")},init:function(a,b,c){c._visOriginal=a.columns().indexes().map(function(b){return a.column(b).visible()}).toArray()},action:function(a,b,c,d){b.columns().every(function(a){a=b.colReorder&&b.colReorder.transpose?b.colReorder.transpose(a,"toOriginal"):a;this.visible(d._visOriginal[a])})}},colvisGroup:{className:"buttons-colvisGroup",action:function(a,b,c,d){b.columns(d.show).visible(!0,!1);b.columns(d.hide).visible(!1,!1);b.columns.adjust()},show:[],hide:[]}});return d.Buttons}); 6 | -------------------------------------------------------------------------------- /static/js/init/peitychart-init.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | 3 | "use strict"; // Start of use strict 4 | 5 | $(function() { 6 | "use strict"; 7 | // Just the defaults. 8 | $("span.pie").peity("pie",{ 9 | width: '50', 10 | height: '50' 11 | }) 12 | 13 | $('span.donut').peity('donut',{ 14 | width: '50', 15 | height: '50' 16 | }) 17 | 18 | 19 | $(".peity-line").peity("line",{ 20 | width: '300', 21 | height: '100' 22 | }) 23 | 24 | $(".bar").peity("bar",{ 25 | width: '300', 26 | height: '100' 27 | }) 28 | 29 | $(".bar-colours-1").peity("bar", { 30 | fill: ["red", "green", "blue"], 31 | width: '100', 32 | height: '100' 33 | }) 34 | 35 | $(".bar-colours-2").peity("bar", { 36 | fill: function(value) { 37 | return value > 0 ? "green" : "red" 38 | }, 39 | width: '100', 40 | height: '100' 41 | }) 42 | 43 | $(".bar-colours-3").peity("bar", { 44 | fill: function(_, i, all) { 45 | var g = parseInt((i / all.length) * 255) 46 | return "rgb(255, " + g + ", 0)" 47 | }, 48 | width: '100', 49 | height: '100' 50 | }) 51 | 52 | $(".pie-colours-1").peity("pie", { 53 | fill: ["cyan", "magenta", "yellow", "black"], 54 | width: '100', 55 | height: '100' 56 | }) 57 | 58 | $(".pie-colours-2").peity("pie", { 59 | fill: function(_, i, all) { 60 | var g = parseInt((i / all.length) * 255) 61 | return "rgb(255, " + g + ", 0)" 62 | }, 63 | width: '100', 64 | height: '100' 65 | }) 66 | 67 | // Using data attributes 68 | $(".data-attributes span").peity("donut") 69 | 70 | // Evented example. 71 | $("select").change(function() { 72 | var text = $(this).val() + "/" + 5 73 | 74 | $(this) 75 | .siblings("span.graph") 76 | .text(text) 77 | .change() 78 | 79 | $("#notice").text("Chart updated: " + text) 80 | }).change() 81 | 82 | $("span.graph").peity("pie") 83 | 84 | // Updating charts. 85 | var updatingChart = $(".updating-chart").peity("line", { width: "100%",height:100 }) 86 | 87 | setInterval(function() { 88 | var random = Math.round(Math.random() * 10) 89 | var values = updatingChart.text().split(",") 90 | values.shift() 91 | values.push(random) 92 | 93 | updatingChart 94 | .text(values.join(",")) 95 | .change() 96 | }, 1000) 97 | }) 98 | 99 | 100 | })(jQuery); 101 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/django 3 | # Edit at https://www.gitignore.io/?templates=django 4 | 5 | ### Django ### 6 | *.log 7 | *.pot 8 | *.pyc 9 | __pycache__/ 10 | local_settings.py 11 | db.sqlite3 12 | media 13 | 14 | # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ 15 | # in your Git repository. Update and uncomment the following line accordingly. 16 | # /staticfiles/ 17 | 18 | ### Django.Python Stack ### 19 | # Byte-compiled / optimized / DLL files 20 | *.py[cod] 21 | *$py.class 22 | 23 | # C extensions 24 | *.so 25 | 26 | # Distribution / packaging 27 | .Python 28 | build/ 29 | develop-eggs/ 30 | dist/ 31 | downloads/ 32 | eggs/ 33 | .eggs/ 34 | lib/ 35 | lib64/ 36 | parts/ 37 | sdist/ 38 | var/ 39 | wheels/ 40 | pip-wheel-metadata/ 41 | share/python-wheels/ 42 | *.egg-info/ 43 | .installed.cfg 44 | *.egg 45 | MANIFEST 46 | 47 | # PyInstaller 48 | # Usually these files are written by a python script from a template 49 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 50 | *.manifest 51 | *.spec 52 | 53 | # Installer logs 54 | pip-log.txt 55 | pip-delete-this-directory.txt 56 | 57 | # Unit test / coverage reports 58 | htmlcov/ 59 | .tox/ 60 | .nox/ 61 | .coverage 62 | .coverage.* 63 | .cache 64 | nosetests.xml 65 | coverage.xml 66 | *.cover 67 | .hypothesis/ 68 | .pytest_cache/ 69 | 70 | # Translations 71 | *.mo 72 | 73 | # Django stuff: 74 | 75 | # Flask stuff: 76 | instance/ 77 | .webassets-cache 78 | 79 | # Scrapy stuff: 80 | .scrapy 81 | 82 | # Sphinx documentation 83 | docs/_build/ 84 | 85 | # PyBuilder 86 | target/ 87 | 88 | # Jupyter Notebook 89 | .ipynb_checkpoints 90 | 91 | # IPython 92 | profile_default/ 93 | ipython_config.py 94 | 95 | # pyenv 96 | .python-version 97 | 98 | # pipenv 99 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 100 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 101 | # having no cross-platform support, pipenv may install dependencies that don’t work, or not 102 | # install all needed dependencies. 103 | #Pipfile.lock 104 | 105 | # celery beat schedule file 106 | celerybeat-schedule 107 | 108 | # SageMath parsed files 109 | *.sage.py 110 | 111 | # Environments 112 | .env 113 | .venv 114 | env/ 115 | venv/ 116 | ENV/ 117 | env.bak/ 118 | venv.bak/ 119 | 120 | # Spyder project settings 121 | .spyderproject 122 | .spyproject 123 | 124 | # Rope project settings 125 | .ropeproject 126 | 127 | # mkdocs documentation 128 | /site 129 | 130 | # mypy 131 | .mypy_cache/ 132 | .dmypy.json 133 | dmypy.json 134 | .vscode/ 135 | # Pyre type checker 136 | .pyre/ 137 | 138 | # End of https://www.gitignore.io/api/django 139 | -------------------------------------------------------------------------------- /templates/libro/autor/autor_formset.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% load staticfiles %} 3 | {% block titulo %} 4 | Biblioteca.dev | Formset Autor 5 | {% endblock titulo %} 6 | {% block extra_css %} 7 | 8 | {% endblock extra_css %} 9 | {% block body %} 10 | 11 |
    12 |
    13 |
    14 |
    15 |

    Creacion de Autores con FormSets

    16 |
    17 |
    18 |
    19 | 20 |
    21 |
    22 | {% csrf_token %} 23 | {{ form.management_form }} 24 | {% for fo in form %} 25 |
    26 | {{ fo }} 27 |
    28 |
    29 | {% endfor %} 30 | 31 |
    32 | 33 |
    34 |
    35 | 36 | {% endblock body %} 37 | {% block extrajs %} 38 | 67 | {% endblock extrajs %} -------------------------------------------------------------------------------- /templates/libro/detalle_libro_disponible.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | {% load staticfiles %} 3 | {% block titulo %} 4 | Biblioteca.dev | {{ object }} 5 | {% endblock titulo %} 6 | {% block extra_css %} 7 | {% endblock extra_css %} 8 | {% block body %} 9 |
    10 |
    11 |
    12 |
    13 | 14 |

    {{ object }}

    15 |
    16 |
    17 |
    18 | 19 |
    20 |
    21 |
    22 | {% csrf_token %} 23 | Card image cap 24 |
    25 |
    26 |
    27 |

    Publicado el: {{ object.fecha_publicacion }}

    28 |
    29 |
    30 |

    Autores

    {{ object.obtener_autores }} 31 |
    32 |
    33 |

    Descripcion

    34 |
    35 |
    36 |

    {{ object.descripcion }}

    37 |
    38 |
    39 |
    40 |
    41 |
    42 | 43 |
    44 |
    45 |
    46 | 47 |
    48 |
    49 | 50 | 51 | {% endblock body %} 52 | {% block extrajs %} 53 | 74 | {% endblock extrajs %} -------------------------------------------------------------------------------- /apps/usuario/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2021-07-18 18:23 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('auth', '0011_update_proxy_permissions'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Rol', 18 | fields=[ 19 | ('id', models.AutoField(primary_key=True, serialize=False)), 20 | ('rol', models.CharField(max_length=50, unique=True, verbose_name='Rol')), 21 | ], 22 | options={ 23 | 'verbose_name': 'Rol', 24 | 'verbose_name_plural': 'Rols', 25 | }, 26 | ), 27 | migrations.CreateModel( 28 | name='Usuario', 29 | fields=[ 30 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 31 | ('password', models.CharField(max_length=128, verbose_name='password')), 32 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 33 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 34 | ('username', models.CharField(max_length=100, unique=True, verbose_name='Nombre de usuario')), 35 | ('email', models.EmailField(max_length=254, unique=True, verbose_name='Correo Electrónico')), 36 | ('nombres', models.CharField(blank=True, max_length=200, null=True, verbose_name='Nombres')), 37 | ('apellidos', models.CharField(blank=True, max_length=200, null=True, verbose_name='Apellidos')), 38 | ('imagen', models.ImageField(blank=True, max_length=200, null=True, upload_to='perfil/', verbose_name='Imagen de Perfil')), 39 | ('is_active', models.BooleanField(default=True)), 40 | ('is_staff', models.BooleanField(default=False)), 41 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), 42 | ('rol', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='usuario.Rol')), 43 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), 44 | ], 45 | options={ 46 | 'permissions': [('permiso_desde_codigo', 'Este es un permiso creado desde código'), ('segundo_permiso_codigo', 'Segundo permiso creado desde codigo')], 47 | }, 48 | ), 49 | ] 50 | -------------------------------------------------------------------------------- /apps/libro/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2021-07-18 18:23 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Autor', 17 | fields=[ 18 | ('id', models.AutoField(primary_key=True, serialize=False)), 19 | ('nombre', models.CharField(max_length=200)), 20 | ('apellidos', models.CharField(max_length=220)), 21 | ('nacionalidad', models.CharField(max_length=100)), 22 | ('descripcion', models.TextField()), 23 | ('estado', models.BooleanField(default=True, verbose_name='Estado')), 24 | ('fecha_creacion', models.DateField(auto_now=True, verbose_name='Fecha de creación')), 25 | ], 26 | options={ 27 | 'verbose_name': 'Autor', 28 | 'verbose_name_plural': 'Autores', 29 | 'ordering': ['nombre'], 30 | }, 31 | ), 32 | migrations.CreateModel( 33 | name='Libro', 34 | fields=[ 35 | ('id', models.AutoField(primary_key=True, serialize=False)), 36 | ('titulo', models.CharField(max_length=255, verbose_name='Título')), 37 | ('fecha_publicacion', models.DateField(verbose_name='Fecha de publicación')), 38 | ('descripcion', models.TextField(blank=True, null=True, verbose_name='Descripción')), 39 | ('cantidad', models.PositiveIntegerField(default=1, verbose_name='Cantidad o Stock')), 40 | ('imagen', models.ImageField(blank=True, max_length=255, null=True, upload_to='libros/', verbose_name='Imagen')), 41 | ('fecha_creacion', models.DateField(auto_now=True, verbose_name='Fecha de creación')), 42 | ('estado', models.BooleanField(default=True, verbose_name='Estado')), 43 | ], 44 | options={ 45 | 'verbose_name': 'Libro', 46 | 'verbose_name_plural': 'Libros', 47 | 'ordering': ['titulo'], 48 | }, 49 | ), 50 | migrations.CreateModel( 51 | name='Reserva', 52 | fields=[ 53 | ('id', models.AutoField(primary_key=True, serialize=False)), 54 | ('cantidad_dias', models.SmallIntegerField(default=7, verbose_name='Cantidad de Dias a Reservar')), 55 | ('fecha_creacion', models.DateField(auto_now_add=True, verbose_name='Fecha de creación')), 56 | ('fecha_vencimiento', models.DateField(blank=True, null=True, verbose_name='Fecha de vencimiento de la reserva')), 57 | ('estado', models.BooleanField(default=True, verbose_name='Estado')), 58 | ('libro', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='libro.Libro')), 59 | ], 60 | options={ 61 | 'verbose_name': 'Reserva', 62 | 'verbose_name_plural': 'Reservas', 63 | }, 64 | ), 65 | ] 66 | -------------------------------------------------------------------------------- /apps/libro/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.core.exceptions import ValidationError 3 | from .models import Autor,Libro,Reserva 4 | 5 | class AutorForm(forms.ModelForm): 6 | class Meta: 7 | model = Autor 8 | fields = ['nombre','apellidos'] 9 | labels = { 10 | 'nombre': 'Nombre del autor', 11 | 'apellidos': 'Apellidos del autor', 12 | 'nacionalidad': 'Nacionalidad del autor', 13 | 'descripcion': 'Pequeña descripción', 14 | } 15 | widgets = { 16 | 'nombre': forms.TextInput( 17 | attrs = { 18 | 'class':'form-control', 19 | 'placeholder':'Ingrese el nombre del autor' 20 | } 21 | ), 22 | 'apellidos': forms.TextInput( 23 | attrs = { 24 | 'class':'form-control', 25 | 'placeholder':'Ingrese los apellidos del autor' 26 | } 27 | ), 28 | 'nacionalidad':forms.TextInput( 29 | attrs = { 30 | 'class':'form-control', 31 | 'placeholder':'Ingrese una nacionalidad para el autor' 32 | } 33 | ), 34 | 'descripcion': forms.Textarea( 35 | attrs = { 36 | 'class':'form-control', 37 | 'placeholder': 'Ingrese una pequeña descripcion para el autor' 38 | } 39 | ) 40 | } 41 | 42 | class ReservaForm(forms.ModelForm): 43 | def __init__(self, *args, **kwargs): 44 | super().__init__(*args, **kwargs) 45 | #self.fields['libro'].queryset = Libro.objects.filter(estado = True,cantidad__gte = 1) 46 | 47 | class Meta: 48 | model = Reserva 49 | fields = '__all__' 50 | 51 | def clean_libro(self): 52 | libro = self.cleaned_data['libro'] 53 | if libro.cantidad < 1: 54 | raise ValidationError('No se puede reservar este libro, deben existir unidades disponibles.') 55 | 56 | return libro 57 | 58 | class LibroForm(forms.ModelForm): 59 | 60 | def __init__(self, *args, **kwargs): 61 | super().__init__(*args, **kwargs) 62 | self.fields['autor_id'].queryset = Autor.objects.filter(estado = True) 63 | 64 | 65 | class Meta: 66 | model = Libro 67 | fields = ('titulo','autor','fecha_publicacion','descripcion','imagen','cantidad') 68 | label = { 69 | 'titulo':'Título del libro', 70 | 'autor_id': 'Autor(es) del Libro', 71 | 'fecha_publicacion': 'Fecha de Publciación del Libro' 72 | } 73 | widgets = { 74 | 'titulo': forms.TextInput( 75 | attrs = { 76 | 'class': 'form-control', 77 | 'placeholder': 'Ingrese título de libro' 78 | } 79 | ), 80 | 'autor_id': forms.SelectMultiple( 81 | attrs = { 82 | 'class':'form-control' 83 | } 84 | ), 85 | 'fecha_publicacion': forms.SelectDateWidget( 86 | attrs = { 87 | 'class': 'form-control' 88 | } 89 | ) 90 | } 91 | -------------------------------------------------------------------------------- /templates/login.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | 5 | 6 | 7 | Inicio de Sesión | BIBLIOTECA.DEV 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /biblioteca/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for biblioteca project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.0.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | from django.urls import reverse_lazy 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'dj&!t6j@th%ecbu%ud70q$)!3&!n8@c_9m%o5(9+iy$p35=c3^' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'import_export', 41 | 'apps.libro', 42 | 'apps.usuario', 43 | ] 44 | 45 | MIDDLEWARE = [ 46 | 'querycount.middleware.QueryCountMiddleware', 47 | 'apps.usuario.middleware.PruebaMiddleware', 48 | 'django.middleware.security.SecurityMiddleware', 49 | 'django.contrib.sessions.middleware.SessionMiddleware', 50 | 'django.middleware.common.CommonMiddleware', 51 | 'django.middleware.csrf.CsrfViewMiddleware', 52 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 53 | 'django.contrib.messages.middleware.MessageMiddleware', 54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 55 | ] 56 | 57 | ROOT_URLCONF = 'biblioteca.urls' 58 | 59 | TEMPLATES = [ 60 | { 61 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 62 | 'DIRS': ['templates'], 63 | 'APP_DIRS': True, 64 | 'OPTIONS': { 65 | 'context_processors': [ 66 | 'django.template.context_processors.debug', 67 | 'django.template.context_processors.request', 68 | 'django.contrib.auth.context_processors.auth', 69 | 'django.contrib.messages.context_processors.messages', 70 | ], 71 | }, 72 | }, 73 | ] 74 | 75 | WSGI_APPLICATION = 'biblioteca.wsgi.application' 76 | 77 | 78 | # Database 79 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases 80 | 81 | DATABASES = { 82 | 'default': { 83 | 'ENGINE': 'django.db.backends.sqlite3', 84 | 'NAME': 'db.sqlite3', 85 | } 86 | } 87 | 88 | 89 | # Password validation 90 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators 91 | 92 | AUTH_PASSWORD_VALIDATORS = [ 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 104 | }, 105 | ] 106 | 107 | AUTH_USER_MODEL = 'usuario.Usuario' 108 | 109 | # Internationalization 110 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ 111 | 112 | LANGUAGE_CODE = 'es-PE' 113 | 114 | TIME_ZONE = 'UTC' 115 | 116 | USE_I18N = True 117 | 118 | USE_L10N = True 119 | 120 | USE_TZ = True 121 | 122 | 123 | # Static files (CSS, JavaScript, Images) 124 | # https://docs.djangoproject.com/en/2.0/howto/static-files/ 125 | 126 | STATIC_URL = '/static/' 127 | STATICFILES_DIRS = (BASE_DIR,'static') 128 | LOGIN_REDIRECT_URL = reverse_lazy('index') 129 | LOGOUT_REDIRECT_URL = reverse_lazy('login') 130 | MEDIA_URL = '/media/' 131 | MEDIA_ROOT = os.path.join(BASE_DIR,'media') -------------------------------------------------------------------------------- /static/js/main.js: -------------------------------------------------------------------------------- 1 | var $ = jQuery.noConflict(); 2 | 3 | jQuery(document).ready(function($) { 4 | 5 | "use strict"; 6 | 7 | [].slice.call( document.querySelectorAll( 'select.cs-select' ) ).forEach( function(el) { 8 | new SelectFx(el); 9 | }); 10 | 11 | jQuery('.selectpicker').selectpicker; 12 | 13 | 14 | 15 | 16 | $('.search-trigger').on('click', function(event) { 17 | event.preventDefault(); 18 | event.stopPropagation(); 19 | $('.search-trigger').parent('.header-left').addClass('open'); 20 | }); 21 | 22 | $('.search-close').on('click', function(event) { 23 | event.preventDefault(); 24 | event.stopPropagation(); 25 | $('.search-trigger').parent('.header-left').removeClass('open'); 26 | }); 27 | 28 | $('.equal-height').matchHeight({ 29 | property: 'max-height' 30 | }); 31 | 32 | // var chartsheight = $('.flotRealtime2').height(); 33 | // $('.traffic-chart').css('height', chartsheight-122); 34 | 35 | 36 | // Counter Number 37 | $('.count').each(function () { 38 | $(this).prop('Counter',0).animate({ 39 | Counter: $(this).text() 40 | }, { 41 | duration: 3000, 42 | easing: 'swing', 43 | step: function (now) { 44 | $(this).text(Math.ceil(now)); 45 | } 46 | }); 47 | }); 48 | 49 | 50 | 51 | 52 | // Menu Trigger 53 | $('#menuToggle').on('click', function(event) { 54 | var windowWidth = $(window).width(); 55 | if (windowWidth<1010) { 56 | $('body').removeClass('open'); 57 | if (windowWidth<760){ 58 | $('#left-panel').slideToggle(); 59 | } else { 60 | $('#left-panel').toggleClass('open-menu'); 61 | } 62 | } else { 63 | $('body').toggleClass('open'); 64 | $('#left-panel').removeClass('open-menu'); 65 | } 66 | 67 | }); 68 | 69 | 70 | $(".menu-item-has-children.dropdown").each(function() { 71 | $(this).on('click', function() { 72 | var $temp_text = $(this).children('.dropdown-toggle').html(); 73 | $(this).children('.sub-menu').prepend('
  • ' + $temp_text + '
  • '); 74 | }); 75 | }); 76 | 77 | 78 | // Load Resize 79 | $(window).on("load resize", function(event) { 80 | var windowWidth = $(window).width(); 81 | if (windowWidth<1010) { 82 | $('body').addClass('small-device'); 83 | } else { 84 | $('body').removeClass('small-device'); 85 | } 86 | 87 | }); 88 | 89 | 90 | }); 91 | function abrir_modal_edicion(url) { 92 | $('#edicion').load(url, function () { 93 | $(this).modal('show'); 94 | }); 95 | } 96 | function abrir_modal_creacion(url) { 97 | $('#creacion').load(url, function () { 98 | $(this).modal('show'); 99 | }); 100 | } 101 | function abrir_modal_eliminacion(url) { 102 | $('#eliminacion').load(url, function () { 103 | $(this).modal('show'); 104 | }); 105 | } 106 | function cerrar_modal_creacion(){ 107 | $('#creacion').modal('hide'); 108 | } 109 | 110 | function cerrar_modal_edicion() { 111 | $('#edicion').modal('hide'); 112 | } 113 | function cerrar_modal_eliminacion() { 114 | $('#eliminacion').modal('hide'); 115 | } 116 | function activarBoton(){ 117 | if($('#boton_creacion').prop('disabled')){ 118 | $('#boton_creacion').prop('disabled',false); 119 | }else{ 120 | $('#boton_creacion').prop('disabled', true); 121 | } 122 | } 123 | 124 | function mostrarErroresCreacion(errores){ 125 | $('#errores').html(""); 126 | let error = ""; 127 | for(let item in errores.responseJSON.error){ 128 | error += '
    ' + errores.responseJSON.error[item] + '
    '; 129 | } 130 | $('#errores').append(error); 131 | } 132 | function mostrarErroresEdicion(errores) { 133 | $('#erroresEdicion').html(""); 134 | let error = ""; 135 | for (let item in errores.responseJSON.error) { 136 | error += '
    ' + errores.responseJSON.error[item] + '
    '; 137 | } 138 | $('#erroresEdicion').append(error); 139 | } 140 | 141 | function notificacionError(mensaje){ 142 | Swal.fire({ 143 | title: 'Error!', 144 | text: mensaje, 145 | icon: 'error' 146 | }) 147 | } 148 | 149 | function notificacionSuccess(mensaje) { 150 | Swal.fire({ 151 | title: 'Buen Trabajo!', 152 | text: mensaje, 153 | icon: 'success' 154 | }) 155 | } -------------------------------------------------------------------------------- /static/js/usuarios/index.js: -------------------------------------------------------------------------------- 1 | function listadoUsuarios(){ 2 | $.ajax({ 3 | url: "/usuarios/listado_usuarios/", 4 | type:"get", 5 | dataType: "json", 6 | success: function(response){ 7 | if($.fn.DataTable.isDataTable('#tabla_usuarios')){ 8 | $('#tabla_usuarios').DataTable().destroy(); 9 | } 10 | $('#tabla_usuarios tbody').html(""); 11 | for(let i = 0;i < response.length;i++){ 12 | let fila = ''; 13 | fila += '' + (i +1 ) + ''; 14 | fila += '' + response[i]["fields"]['username'] + ''; 15 | fila += '' + response[i]["fields"]['nombres'] + ''; 16 | fila += '' + response[i]["fields"]['apellidos'] + ''; 17 | fila += '' + response[i]["fields"]['email'] + ''; 18 | fila += ''; 20 | fila += ''; 23 | fila += ''; 20 | fila += '