Edoruin commited on
Commit
c560adf
·
1 Parent(s): 2898c04

sending binary files

Browse files
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ app/static/models/*
2
+ __pycache__/
3
+ *.pyc
4
+ .env
Dockerfile CHANGED
@@ -11,6 +11,19 @@ WORKDIR /app
11
  COPY app/requirements.txt .
12
  RUN pip install --no-cache-dir -r requirements.txt
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  # Copiar la aplicación
15
  COPY app/ .
16
 
 
11
  COPY app/requirements.txt .
12
  RUN pip install --no-cache-dir -r requirements.txt
13
 
14
+ # Descargar modelos de Face API durante el build
15
+ RUN mkdir -p app/static/models && \
16
+ apt-get update && apt-get install -y wget && \
17
+ wget -P app/static/models/ \
18
+ https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/tiny_face_detector_model-weights_manifest.json \
19
+ https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/tiny_face_detector_model-shard1 \
20
+ https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/face_landmark_68_model-weights_manifest.json \
21
+ https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/face_landmark_68_model-shard1 \
22
+ https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/face_recognition_model-weights_manifest.json \
23
+ https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/face_recognition_model-shard1 \
24
+ https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights/face_recognition_model-shard2 \
25
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
26
+
27
  # Copiar la aplicación
28
  COPY app/ .
29
 
app/main.py CHANGED
@@ -697,6 +697,10 @@ def classroom():
697
  def asistencia():
698
  return render_template('asistencia.html', title="Toma de Asistencia")
699
 
 
 
 
 
700
  @app.route('/api/faces', methods=['GET', 'POST'])
701
  def api_faces():
702
  if request.method == 'POST':
 
697
  def asistencia():
698
  return render_template('asistencia.html', title="Toma de Asistencia")
699
 
700
+ @app.route('/tutoria')
701
+ def tutoria():
702
+ return render_template('tutoria.html', title="Tutoría y Guías")
703
+
704
  @app.route('/api/faces', methods=['GET', 'POST'])
705
  def api_faces():
706
  if request.method == 'POST':
app/static/css/style.css CHANGED
@@ -70,24 +70,96 @@ body {
70
  gap: 0.5rem;
71
  }
72
 
73
- .nav-links {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  display: flex;
75
- gap: 2rem;
 
 
 
 
 
 
76
  }
77
 
78
- .nav-item {
 
 
 
79
  color: var(--text-dim);
80
- text-decoration: none;
81
- font-weight: 600;
 
82
  transition: color 0.3s;
83
- font-size: 0.9rem;
84
- letter-spacing: 1px;
85
  }
86
 
87
- .nav-item:hover {
88
  color: var(--accent);
89
  }
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  .content-wrapper {
92
  max-width: 1000px;
93
  margin: 0 auto;
 
70
  gap: 0.5rem;
71
  }
72
 
73
+ /* MENÚ HAMBURGUESA */
74
+ .hamburger {
75
+ background: none;
76
+ border: none;
77
+ color: var(--accent);
78
+ font-size: 1.8rem;
79
+ cursor: pointer;
80
+ transition: transform 0.3s;
81
+ z-index: 200;
82
+ }
83
+
84
+ .hamburger:hover {
85
+ transform: scale(1.1);
86
+ }
87
+
88
+ /* SIDEBAR OVERLAY */
89
+ .nav-overlay {
90
+ position: fixed;
91
+ top: 0;
92
+ left: 0;
93
+ width: 100%;
94
+ height: 100%;
95
+ background: rgba(0, 0, 0, 0.6);
96
+ backdrop-filter: blur(4px);
97
+ z-index: 1000;
98
+ opacity: 0;
99
+ visibility: hidden;
100
+ transition: all 0.3s;
101
+ }
102
+
103
+ .nav-overlay.active {
104
+ opacity: 1;
105
+ visibility: visible;
106
+ }
107
+
108
+ .nav-sidebar {
109
+ position: fixed;
110
+ top: 0;
111
+ right: -300px;
112
+ width: 280px;
113
+ height: 100%;
114
+ background: rgba(15, 23, 42, 0.95);
115
+ padding: 2rem;
116
  display: flex;
117
+ flex-direction: column;
118
+ transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1);
119
+ border-left: 1px solid var(--glass-border);
120
+ }
121
+
122
+ .nav-overlay.active .nav-sidebar {
123
+ right: 0;
124
  }
125
 
126
+ .close-btn {
127
+ align-self: flex-end;
128
+ background: none;
129
+ border: none;
130
  color: var(--text-dim);
131
+ font-size: 2rem;
132
+ cursor: pointer;
133
+ margin-bottom: 2rem;
134
  transition: color 0.3s;
 
 
135
  }
136
 
137
+ .close-btn:hover {
138
  color: var(--accent);
139
  }
140
 
141
+ .sidebar-links {
142
+ display: flex;
143
+ flex-direction: column;
144
+ gap: 1.5rem;
145
+ height: 100%;
146
+ }
147
+
148
+ .sidebar-links .nav-item {
149
+ font-size: 1.1rem;
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 1rem;
153
+ padding: 0.5rem;
154
+ border-radius: 8px;
155
+ transition: background 0.3s;
156
+ }
157
+
158
+ .sidebar-links .nav-item:hover {
159
+ background: rgba(255, 255, 255, 0.05);
160
+ padding-left: 1rem;
161
+ }
162
+
163
  .content-wrapper {
164
  max-width: 1000px;
165
  margin: 0 auto;
app/templates/asistencia.html CHANGED
@@ -63,7 +63,8 @@
63
  // Cargar modelos y arrancar
64
  async function init() {
65
  try {
66
- const MODEL_URL = 'https://raw.githubusercontent.com/justadudewhohacks/face-api.js/master/weights';
 
67
  await Promise.all([
68
  faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),
69
  faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),
 
63
  // Cargar modelos y arrancar
64
  async function init() {
65
  try {
66
+ // Modelos locales
67
+ const MODEL_URL = '/static/models';
68
  await Promise.all([
69
  faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),
70
  faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),
app/templates/base.html CHANGED
@@ -34,14 +34,32 @@
34
  <i class="fas fa-cube"></i>
35
  <span>MAKER SPACE</span>
36
  </a>
37
- <div class="nav-links">
38
- <a href="/prestamos" class="nav-item">PRÉSTAMOS</a>
39
- <a href="/miembros" class="nav-item">MIEMBROS</a>
40
-
41
- <!-- Botón de Logout (solo si el usuario está autenticado) -->
42
- {% if current_user.is_authenticated %}
43
- <a href="/logout" class="nav-item" style="color: #ef4444;"><i class="fas fa-sign-out-alt"></i></a>
44
- {% endif %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  </div>
46
  <div style="display: flex; gap: 1rem; align-items: center;">
47
  <!-- Botón para instalar PWA (solo aparece si es instalable) -->
@@ -143,25 +161,21 @@
143
  });
144
  }
145
 
146
- // Manejo del banner de instalación "Añadir a pantalla de inicio"
147
- let deferredPrompt;
148
- const installBtn = document.getElementById('install-btn');
 
149
 
150
- window.addEventListener('beforeinstallprompt', (e) => {
151
- e.preventDefault();
152
- deferredPrompt = e;
153
- installBtn.style.display = 'block';
154
- });
155
 
156
- installBtn.addEventListener('click', async () => {
157
- if (deferredPrompt) {
158
- deferredPrompt.prompt();
159
- const { outcome } = await deferredPrompt.userChoice;
160
- if (outcome === 'accepted') {
161
- installBtn.style.display = 'none';
162
- }
163
- deferredPrompt = null;
164
- }
165
  });
166
  </script>
167
  </body>
 
34
  <i class="fas fa-cube"></i>
35
  <span>MAKER SPACE</span>
36
  </a>
37
+ <!-- Botón Hamburguesa -->
38
+ <button class="hamburger" id="hamburger-btn" aria-label="Menu">
39
+ <i class="fas fa-bars"></i>
40
+ </button>
41
+
42
+ <!-- Sidebar Overlay -->
43
+ <div class="nav-overlay" id="nav-overlay">
44
+ <div class="nav-sidebar glass">
45
+ <button class="close-btn" id="close-btn">&times;</button>
46
+ <div class="sidebar-links">
47
+ <a href="/" class="nav-item"><i class="fas fa-home"></i> INICIO</a>
48
+ <a href="/tutoria" class="nav-item"><i class="fas fa-book"></i> TUTORÍA</a>
49
+ <a href="/prestamos" class="nav-item"><i class="fas fa-tools"></i> PRÉSTAMOS</a>
50
+ <a href="/miembros" class="nav-item"><i class="fas fa-users"></i> MIEMBROS</a>
51
+
52
+ {% if current_user.is_authenticated %}
53
+ <div style="margin-top: auto; border-top: 1px solid rgba(255,255,255,0.1); padding-top: 1rem;">
54
+ <a href="/logout" class="nav-item" style="color: #ef4444;"><i
55
+ class="fas fa-sign-out-alt"></i> Salir</a>
56
+ </div>
57
+ {% else %}
58
+ <a href="/login" class="nav-item" style="color: #38bdf8;"><i class="fas fa-sign-in-alt"></i>
59
+ Acceder</a>
60
+ {% endif %}
61
+ </div>
62
+ </div>
63
  </div>
64
  <div style="display: flex; gap: 1rem; align-items: center;">
65
  <!-- Botón para instalar PWA (solo aparece si es instalable) -->
 
161
  });
162
  }
163
 
164
+ // Lógica del Menú Hamburguesa
165
+ const hamburgerBtn = document.getElementById('hamburger-btn');
166
+ const closeBtn = document.getElementById('close-btn');
167
+ const navOverlay = document.getElementById('nav-overlay');
168
 
169
+ function toggleMenu() {
170
+ navOverlay.classList.toggle('active');
171
+ }
 
 
172
 
173
+ if (hamburgerBtn) hamburgerBtn.addEventListener('click', toggleMenu);
174
+ if (closeBtn) closeBtn.addEventListener('click', toggleMenu);
175
+
176
+ // Cerrar al hacer click fuera del sidebar
177
+ if (navOverlay) navOverlay.addEventListener('click', (e) => {
178
+ if (e.target === navOverlay) toggleMenu();
 
 
 
179
  });
180
  </script>
181
  </body>
app/templates/tutoria.html ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block content %}
4
+ <div class="hero section">
5
+ <h1>TUTORIALES</h1>
6
+ <p class="text-dim">Guías y recursos para miembros y maestros del Maker Space.</p>
7
+ </div>
8
+
9
+ <div class="glass" style="padding: 2rem; border-radius: 1rem; margin-bottom: 2rem;">
10
+ <div class="tabs"
11
+ style="display: flex; gap: 1rem; border-bottom: 1px solid rgba(255,255,255,0.1); margin-bottom: 2rem;">
12
+ <button class="tab-btn active" onclick="switchTab('miembros')" id="tab-miembros"
13
+ style="background: none; border: none; color: white; padding: 1rem; cursor: pointer; border-bottom: 2px solid #38bdf8;">
14
+ Para Miembros
15
+ </button>
16
+ <button class="tab-btn" onclick="switchTab('maestros')" id="tab-maestros"
17
+ style="background: none; border: none; color: rgba(255,255,255,0.5); padding: 1rem; cursor: pointer;">
18
+ Para Maestros
19
+ </button>
20
+ </div>
21
+
22
+ <div id="content-miembros" class="tab-content">
23
+ <ul style="list-style: none; padding: 0;">
24
+ <li class="repo-item glass">
25
+ <div>
26
+ <h3>🚀 Primeros Pasos</h3>
27
+ <p class="text-dim">Cómo registrarte y acceder al espacio.</p>
28
+ </div>
29
+ <button class="btn glass">Ver Guía</button>
30
+ </li>
31
+ <li class="repo-item glass">
32
+ <div>
33
+ <h3>🛠 Solicitud de Herramientas</h3>
34
+ <p class="text-dim">Proceso para pedir prestada una herramienta.</p>
35
+ </div>
36
+ <button class="btn glass">Ver Guía</button>
37
+ </li>
38
+ </ul>
39
+ </div>
40
+
41
+ <div id="content-maestros" class="tab-content" style="display: none;">
42
+ <ul style="list-style: none; padding: 0;">
43
+ <li class="repo-item glass">
44
+ <div>
45
+ <h3>📋 Gestión de Asistencia</h3>
46
+ <p class="text-dim">Cómo usar el sistema de reconocimiento facial.</p>
47
+ </div>
48
+ <button class="btn glass">Ver Guía</button>
49
+ </li>
50
+ <li class="repo-item glass">
51
+ <div>
52
+ <h3>📦 Aprobación de Préstamos</h3>
53
+ <p class="text-dim">Guía para administrar solicitudes de equipo.</p>
54
+ </div>
55
+ <button class="btn glass">Ver Guía</button>
56
+ </li>
57
+ </ul>
58
+ </div>
59
+ </div>
60
+
61
+ <script>
62
+ function switchTab(tabName) {
63
+ document.querySelectorAll('.tab-content').forEach(el => el.style.display = 'none');
64
+ document.querySelectorAll('.tab-btn').forEach(el => {
65
+ el.style.color = 'rgba(255,255,255,0.5)';
66
+ el.style.borderBottom = 'none';
67
+ el.classList.remove('active');
68
+ });
69
+
70
+ document.getElementById(`content-${tabName}`).style.display = 'block';
71
+ const activeTab = document.getElementById(`tab-${tabName}`);
72
+ activeTab.style.color = 'white';
73
+ activeTab.style.borderBottom = '2px solid #38bdf8';
74
+ activeTab.classList.add('active');
75
+ }
76
+ </script>
77
+ {% endblock %}