New things
This commit is contained in:
@@ -12,11 +12,10 @@ require_once "../include/bd_pdo.php";
|
||||
global $pdo;
|
||||
//print_r($_POST);
|
||||
if (!isset($_POST['periodo']) || count($_POST["periodo"])==0) {
|
||||
//header("Location: ../días_festivos.php?error=1");
|
||||
echo "Error no hay periodo";
|
||||
header("Location: ../días_festivos.php?error=0");
|
||||
exit();
|
||||
}
|
||||
$periodo = $_POST['periodo'];
|
||||
$periodoArr = $_POST['periodo'];
|
||||
|
||||
if (isset($_POST['rango'])) {
|
||||
$diaInicio = new DateTime(date("Y-m-d", strtotime(str_replace("/", "-", $_POST['diaFestivo']))));
|
||||
@@ -24,27 +23,35 @@ if (isset($_POST['rango'])) {
|
||||
$cantidad = $diaFin->diff($diaInicio);
|
||||
$date = date("Y-m-d", strtotime(str_replace("/", "-", $_POST['diaFestivo'])));
|
||||
for ($dias = 0; $dias <= $cantidad->days; $dias++) {
|
||||
$sql = "SELECT fi_diasfestivos(:periodo, :dia)";
|
||||
|
||||
$db->querySingle('SELECT fi_diasfestivos({'.implode(",",$fieldName).'}, :dia)', [':dia' => $date]);
|
||||
/*$sql = "SELECT fi_diasfestivos(:periodo, :dia)";
|
||||
$params = [':periodo' => $periodo, ':dia' => $date];
|
||||
query($sql, $params, false);
|
||||
query($sql, $params, false);*/
|
||||
|
||||
$date = date("Y-m-d", strtotime($date . "+ 1 days"));
|
||||
}
|
||||
header("Location: ../días_festivos.php");
|
||||
exit();
|
||||
} else {
|
||||
$sql = "SELECT * FROM fs_diasfestivos(null, :dia)";
|
||||
/*$sql = "SELECT * FROM fs_diasfestivos(null, :dia)";
|
||||
$params = [':dia' => $_POST['diaFestivo']];
|
||||
$dia_general = query($sql, $params, false);
|
||||
$sql = "SELECT * FROM fs_diasfestivos(null, null, :periodo, :dia)";
|
||||
$params = [':periodo' => $periodo, ":dia" => $_POST['diaFestivo']];
|
||||
$dia = query($sql, $params, false);
|
||||
if (!$dia && !$dia_general) { //no hay repetidos
|
||||
$sql = "SELECT fi_diasfestivos(:periodo, :dia)";
|
||||
$id = query($sql, $params, false);
|
||||
$dia = query($sql, $params, false);*/
|
||||
//if (!$dia && !$dia_general) { //no hay repetidos
|
||||
foreach($periodoArr as $periodo){
|
||||
$db->querySingle('SELECT fi_diasfestivos({'.implode(",",$fieldName).'}, :dia)', [':dia' => $_POST['diaFestivo']]);
|
||||
/*$sql = "SELECT fi_diasfestivos(:periodo, :dia)";
|
||||
$params = [':periodo' => $periodo, ":dia" => $_POST['diaFestivo']];
|
||||
$id = query($sql, $params, false);*/
|
||||
}
|
||||
header("Location: ../días_festivos.php");
|
||||
exit();
|
||||
} else {
|
||||
|
||||
/*} else {
|
||||
header("Location: ../días_festivos.php?error=1");
|
||||
exit();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
135
action/profesor_faltas.php
Normal file
135
action/profesor_faltas.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?
|
||||
require_once "{$_SERVER['DOCUMENT_ROOT']}/class/c_login.php";
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!Login::is_logged()) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
echo json_encode(['error' => 'No se ha iniciado sesión']);
|
||||
exit();
|
||||
}
|
||||
$user = Login::get_user();
|
||||
|
||||
try {
|
||||
switch ($_SERVER['REQUEST_METHOD']) {
|
||||
case 'GET':
|
||||
$facultad = $_GET['facultad'] ?? $user->facultad['facultad_id'] ?? null;
|
||||
$porcentaje = $_GET['porcentaje'] ?? null;
|
||||
$faltas = $_GET['faltas'] ?? null;
|
||||
|
||||
if (!isset($facultad) || !is_numeric($facultad)) {
|
||||
$error = 'No se ha seleccionado una facultad';
|
||||
} else if ((!isset($faltas) || !is_numeric($faltas)) && (!isset($porcentaje) || !is_numeric($porcentaje))) {
|
||||
$error = 'Debe especificar las faltas o el porcentaje';
|
||||
} else if (isset($faltas) && (!is_numeric($faltas) || $faltas <= 0)) {
|
||||
$error = 'Las faltas deben ser un número mayor a 0';
|
||||
} else if (isset($porcentaje) && (!is_numeric($porcentaje) || $porcentaje <= 0)) {
|
||||
$error = 'El porcentaje debe ser un número mayor a 0';
|
||||
} else if (isset($faltas) && isset($porcentaje)) {
|
||||
$error = 'No se puede especificar las faltas y el porcentaje al mismo tiempo';
|
||||
} else if (!isset($facultad) || !is_numeric($facultad)) {
|
||||
$error = 'Debe especificar una facultad';
|
||||
}
|
||||
|
||||
if (isset($error)) {
|
||||
header('HTTP/1.1 400 Bad Request');
|
||||
echo json_encode(['error' => $error]);
|
||||
exit();
|
||||
}
|
||||
// Initialize the data array
|
||||
$data = array();
|
||||
|
||||
// Check if 'profesor' or 'supervisor' is set and prepare the specific part of the SQL query accordingly.
|
||||
if (isset($_GET['profesor']) || isset($_GET['supervisor'])) {
|
||||
|
||||
$condition = isset($_GET['profesor'])
|
||||
? "r.registro_fecha IS NULL AND NOT COALESCE(r.registro_justificada, FALSE)"
|
||||
: "estado_supervisor_id = 2";
|
||||
|
||||
$filter = isset($faltas)
|
||||
? "afcp.faltas >= :faltas"
|
||||
: "afcp.porcentaje >= :porcentaje";
|
||||
|
||||
// Prepare the SQL query with placeholders for parameters
|
||||
$data = array_column($db->query(
|
||||
"WITH fechas AS (
|
||||
SELECT
|
||||
fcc.registro_fecha_ideal,
|
||||
fcc.horario_id,
|
||||
hp.profesor_id
|
||||
FROM fechas_clase_cache fcc
|
||||
JOIN horario_profesor hp USING (horario_id)
|
||||
JOIN horario h USING (horario_id)
|
||||
WHERE (h.PERIODO_ID, h.FACULTAD_ID) = (:periodo_id, :facultad_id) and profesor_id <> 0
|
||||
),
|
||||
asistencia_faltas AS (
|
||||
SELECT
|
||||
f.profesor_id,
|
||||
COUNT(1) AS total,
|
||||
COUNT(1) FILTER (WHERE $condition AND f.registro_fecha_ideal <= current_date) AS faltas
|
||||
FROM fechas f
|
||||
LEFT JOIN registro r USING (registro_fecha_ideal, horario_id, profesor_id)
|
||||
GROUP BY f.profesor_id
|
||||
),
|
||||
asistencia_faltas_con_porcentaje AS (
|
||||
SELECT
|
||||
af.profesor_id,
|
||||
af.faltas,
|
||||
af.total,
|
||||
CASE
|
||||
WHEN af.total > 0 THEN ROUND((af.faltas::NUMERIC / af.total) * 100, 2)
|
||||
ELSE NULL
|
||||
END AS porcentaje
|
||||
FROM asistencia_faltas af
|
||||
WHERE af.faltas > 0
|
||||
)
|
||||
SELECT
|
||||
json_build_object(
|
||||
'profesor', json_build_object(
|
||||
'profesor_nombre', p.profesor_nombre,
|
||||
'profesor_clave', p.profesor_clave,
|
||||
'profesor_correo', p.profesor_correo
|
||||
),
|
||||
'profesor_id', afcp.profesor_id,
|
||||
'faltas', afcp.faltas,
|
||||
'total', afcp.total,
|
||||
'porcentaje', afcp.porcentaje
|
||||
) AS result_json
|
||||
FROM asistencia_faltas_con_porcentaje afcp
|
||||
JOIN profesor p USING (profesor_id)
|
||||
WHERE $filter
|
||||
ORDER BY afcp.porcentaje DESC",
|
||||
[
|
||||
'periodo_id' => $user->periodo_id,
|
||||
'facultad_id' => $facultad,
|
||||
] + (isset($faltas)
|
||||
? ['faltas' => $faltas]
|
||||
: ['porcentaje' => $porcentaje])
|
||||
), 'result_json');
|
||||
} else {
|
||||
// Send a 400 Bad Request header and an error message in JSON format
|
||||
header('HTTP/1.1 400 Bad Request');
|
||||
echo json_encode(['error' => 'Especifique si las faltas son de profesor o supervisor']);
|
||||
exit();
|
||||
}
|
||||
if (empty($data)) {
|
||||
header('HTTP/1.1 404 Not Found');
|
||||
echo json_encode(['error' => 'No se encontraron faltas']);
|
||||
} else {
|
||||
echo json_encode(
|
||||
array_map(fn($item) => json_decode($item), $data)
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
header('HTTP/1.1 405 Method Not Allowed');
|
||||
echo json_encode(['error' => 'Método no permitido']);
|
||||
break;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode([
|
||||
'error' => $e->getMessage(),
|
||||
'query' => $db->getLastQuery(),
|
||||
]);
|
||||
}
|
||||
@@ -33,26 +33,29 @@ if(isset($_POST["salon"]) && $_POST["salon"] != "")
|
||||
|
||||
//--------------
|
||||
//Obtiene datos reposición
|
||||
//TODO , SALÓN SALIÓ PENDIENTE Y FALTA REVISAR LISTA DE CORREOS TO
|
||||
$reposicion_rs = $db->querySingle('SELECT h.materia, r.fecha_nueva, r.hora_nueva, r.fecha_clase, h.horario_hora, h.facultad_id, h.facultad, f.clave_dependencia, s.salon_id, s.salon_array, r.motivo_cancelacion, ta.tipoaula_supervisor , ta.tipoaula_nombre
|
||||
$reposicion_rs = $db->querySingle('SELECT h.materia, r.fecha_nueva, r.hora_nueva, r.fecha_clase, h.horario_hora, h.facultad_id, h.facultad, f.clave_dependencia, r.motivo_cancelacion, ta.tipoaula_supervisor , ta.tipoaula_nombre
|
||||
from reposicion_solicitud r
|
||||
inner join horario_view h on h.horario_id = r.horario_id
|
||||
inner join facultad f on f.facultad_id = h.facultad_id
|
||||
inner join tipoaula ta on ta.tipoaula_id = r.tipoaula_id
|
||||
left join salon_view s on r.salon_id = s.salon_id
|
||||
where r.reposicion_solicitud_id = :id_repo',
|
||||
[':id_repo' => $id_repo]
|
||||
);
|
||||
|
||||
if($reposicion_rs["salon_id"] == "" || $reposicion_rs["salon_id"] == NULL){
|
||||
//Obtiene datos de salón asignado
|
||||
$salon_rs = $db->querySingle('SELECT s.salon_id, s.salon_array FROM salon_view s where s.salon_id = :id_salon',
|
||||
[':id_salon' => $salon]
|
||||
);
|
||||
if($salon_rs["salon_id"] == "" || $salon_rs["salon_id"] == NULL){
|
||||
$salon_desc = "Pendiente";
|
||||
}else{
|
||||
$salon_json = json_decode($reposicion_rs["salon_array"], true);
|
||||
$salon_json = json_decode($salon_rs["salon_array"], true);
|
||||
if($salon_json[0]== "UNIVERSIDAD LA SALLE"){
|
||||
unset($salon_json[0]);
|
||||
}
|
||||
$salon_desc = join(" / ",$salon_json);
|
||||
}
|
||||
|
||||
//Obtiene correos
|
||||
$correos_rs = $db->query('SELECT p.profesor_nombre, p.profesor_correo, u.usuario_nombre as jefe_nombre, u.usuario_correo as jefe_correo,
|
||||
coor.usuario_nombre as coordinador_nombre, coor.usuario_correo as coordinador_correo
|
||||
@@ -163,12 +166,12 @@ if($to!= "" && ENVIO_CORREOS){
|
||||
</body>';
|
||||
|
||||
require_once('../include/phpmailer/PHPMailerAutoload.php');
|
||||
/*if(DB_NAME == "poad_pruebas"){
|
||||
if($_ENV['DB_NAME'] == "paad_pruebas"){
|
||||
$asunto = "PRUEBAS-".$asunto;
|
||||
Mailer::enviarCorreo("alejandro.lara@lasalle.mx", $asunto, $texto, true);
|
||||
}else{*/
|
||||
}else{
|
||||
Mailer::enviarCorreo($to, $asunto, $texto, true);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -39,6 +39,7 @@ else
|
||||
$comentario = trim(htmlspecialchars($_POST["comentario"], ENT_QUOTES, "UTF-8"));//limpia texto
|
||||
|
||||
|
||||
|
||||
$duracion_rs = $db->querySingle("select * from duracion where duracion_id = :id", [":id"=>$duracion_id]);
|
||||
$duracion_tiempo = $duracion_rs["duracion_interval"];
|
||||
|
||||
@@ -73,6 +74,9 @@ if(intval($dia) != intval($dia_falta)){
|
||||
exit();
|
||||
}
|
||||
|
||||
//Obtiene materia
|
||||
$materia_rs = $db->querySingle('SELECT materia_nombre from materia where materia_id = :mat',[':mat' => $materia]);
|
||||
|
||||
//Obtiene correo
|
||||
$correos_rs = $db->querySingle('SELECT coor.usuario_correo, coor.usuario_nombre from usuario coor where rol_id = :rol_coord and facultad_id = (
|
||||
select coalesce(facultad_id,0) from usuario u where u.usuario_id = :id_usr)',[':rol_coord' => COORDINADOR, ':id_usr' => $user->user["id"]]
|
||||
@@ -98,6 +102,7 @@ if($tipo == 1){//Reposición
|
||||
if($traslape){
|
||||
//print_r($_POST);
|
||||
//echo "SELECT * from traslape_profesor_reposicion($prof,'".DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')."' , '$hora', $duracion)";
|
||||
|
||||
header("Location:".$pag."?error=9");
|
||||
exit();
|
||||
}
|
||||
@@ -110,12 +115,13 @@ if($tipo == 1){//Reposición
|
||||
]
|
||||
);
|
||||
}catch(Exception $e){
|
||||
|
||||
echo $e->getMessage();
|
||||
//header("Location: ".$pag."?error=1");
|
||||
exit();
|
||||
}
|
||||
$texto = "<p>Se creó una reposición nueva.</p>";
|
||||
$texto .= "<p><b>".mb_strtoupper($reposicion_rs["materia"])."</b> del día <b>".$fecha_falta." a las ".$hor." hrs. </b> se propone reponer el <b>".$fecha_new." a las ".$hora." hrs.</b>";
|
||||
$texto .= "<p><b>".mb_strtoupper($materia_rs["materia_nombre"])."</b> del día <b>".$fecha_falta." a las ".$hor." hrs. </b> se propone reponer el <b>".$fecha_new." a las ".$hora." hrs.</b>";
|
||||
$texto .= "<p>Ingresa al <a href='https://paad.lci.ulsa.mx'>sistema PAAD</a> para autorizarla.</p>";
|
||||
|
||||
/*
|
||||
@@ -134,11 +140,12 @@ if($tipo == 1){//Reposición
|
||||
]
|
||||
);
|
||||
}catch(Exception $e){
|
||||
|
||||
header("Location: ".$pag."?error=1");
|
||||
exit();
|
||||
}
|
||||
$texto = "<p>Se creó un cambio de salón nuevo.</p>";
|
||||
$texto .= "<p><b>".mb_strtoupper($reposicion_rs["materia"])."</b> del día <b>".$fecha_falta." a las ".$hora." hrs. </b> se propone reponer el <b>".$fecha_nueva." a las ".$hora_nueva." hrs.</b>";
|
||||
$texto .= "<p><b>".mb_strtoupper($materia_rs["materia_nombre"])."</b> del día <b>".$fecha_falta." a las ".$hora." hrs. </b> se propone reponer el <b>".$fecha_nueva." a las ".$hora_nueva." hrs.</b>";
|
||||
$texto .= "<p>Ingresa al <a href='https://paad.lci.ulsa.mx'>sistema PAAD</a> para autorizarlo.</p>";
|
||||
|
||||
/*
|
||||
@@ -159,14 +166,14 @@ if($to!= "" && ENVIO_CORREOS){
|
||||
</body>';
|
||||
|
||||
require_once('../include/phpmailer/PHPMailerAutoload.php');
|
||||
/*if(DB_NAME == "poad_pruebas"){
|
||||
if($_ENV['DB_NAME'] == "paad_pruebas"){
|
||||
$asunto = "PRUEBAS-".$asunto;
|
||||
Mailer::enviarCorreo("alejandro.lara@lasalle.mx", $asunto, $texto, true);
|
||||
}else{*/
|
||||
}else{
|
||||
Mailer::enviarCorreo($to, $asunto, $texto, true);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
header("Location: ".$pag."?ok=0");
|
||||
exit();
|
||||
header("Location: ".$pag."?ok=0");
|
||||
?>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
class Mailer{
|
||||
private const FROM = "academia@lasalle.mx";
|
||||
private const FROM_NAME = "Vicerrectoría Académica";
|
||||
private const FROM_PASS = "Foy25193";
|
||||
private const FROM_PASS = "4c4d3m14S3gur4##";//Foy25193
|
||||
private const FOOTER = "<p style='margin-top:5em; color:#aaa;font-style:italics'><small>Este es un correo automatizado, esta cuenta no recibe correos.<small></p>";
|
||||
//private $lista_to, $asunto, $texto;
|
||||
|
||||
@@ -50,12 +50,6 @@ class Mailer{
|
||||
}else{//cadena de texto separada por ;
|
||||
if(strpos($lista_to, ";")!==false){
|
||||
$toArr = explode(";", $lista_to);
|
||||
}elseif(strpos($lista_to, ",")!==false){
|
||||
$toArr = explode(",", $lista_to);
|
||||
}else{
|
||||
echo "Cadena de correos inválida";
|
||||
return false;
|
||||
}
|
||||
foreach($toArr as $correo){
|
||||
if(trim($correo)!=""){
|
||||
if($bcc)
|
||||
@@ -64,10 +58,32 @@ class Mailer{
|
||||
$mail->AddAddress($correo);
|
||||
}
|
||||
}
|
||||
}elseif(strpos($lista_to, ",")!==false){
|
||||
$toArr = explode(",", $lista_to);
|
||||
foreach($toArr as $correo){
|
||||
if(trim($correo)!=""){
|
||||
if($bcc)
|
||||
$mail->addBCC($correo);
|
||||
else
|
||||
$mail->AddAddress($correo);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(trim($lista_to)!=""){
|
||||
if($bcc)
|
||||
$mail->addBCC($lista_to);
|
||||
else
|
||||
$mail->AddAddress($lista_to);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//Success
|
||||
if ($mail->Send()) {
|
||||
return true;
|
||||
}else{
|
||||
echo "Error al enviar correo";
|
||||
return false;
|
||||
}
|
||||
}catch(phpmailerException $e){
|
||||
echo $mail->ErrorInfo;
|
||||
|
||||
142
export/faltas_excel.php
Normal file
142
export/faltas_excel.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
$fecha = date('d_m_Y');
|
||||
header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
header("Content-Disposition: attachment;filename=horario_$fecha.xlsx");
|
||||
header("Cache-Control: max-age=0");
|
||||
|
||||
require_once "../vendor/autoload.php";
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Color;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
// Image settings
|
||||
$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
|
||||
$drawing->setName('La Salle')
|
||||
->setDescription('La Salle')
|
||||
->setPath('../imagenes/logo.png')
|
||||
->setCoordinates('B1')
|
||||
->setHeight(100)
|
||||
->setOffsetX(10)
|
||||
->setWorksheet($spreadsheet->getActiveSheet());
|
||||
|
||||
$json = file_get_contents('php://input');
|
||||
$data = json_decode($json, true);
|
||||
|
||||
empty($data) and die(json_encode(['error' => 'No se recibieron datos', 'data' => $data]));
|
||||
|
||||
$data_excel = array(
|
||||
"CLAVE" => 'profesor_clave',
|
||||
"PROFESOR" => 'profesor_nombre',
|
||||
"CORREO" => 'profesor_correo',
|
||||
"FALTAS" => 'faltas',
|
||||
"TOTAL" => 'total',
|
||||
"PORCENTAJE" => 'porcentaje',
|
||||
); // Same as before
|
||||
|
||||
|
||||
|
||||
const ROW = 6;
|
||||
// Merge cells from A1 to C+ ROW
|
||||
$sheet->mergeCells('A1:B' . (ROW - 1));
|
||||
// Merge cells from D1 to size of $data_excel + 1
|
||||
$sheet->mergeCells('C1:' . chr(65 + count($data_excel) - 1) . (ROW - 1));
|
||||
|
||||
// Set the title in D1 Sistema de Auditoría de Asistencia
|
||||
$sheet->setCellValue('C1', 'Sistema de Auditoría de Asistencia');
|
||||
$sheet->getStyle('C1')->applyFromArray([
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 30,
|
||||
'name' => 'Indivisa Text Sans',
|
||||
'color' => ['argb' => '001d68'],
|
||||
],
|
||||
'alignment' => [
|
||||
'vertical' => Alignment::VERTICAL_CENTER,
|
||||
],
|
||||
]);
|
||||
|
||||
|
||||
$lastColumnLetter = chr(65 + count($data_excel) - 1);
|
||||
$headers_range = 'A' . ROW . ':' . $lastColumnLetter . ROW;
|
||||
|
||||
$keys = array_keys($data_excel);
|
||||
array_walk($keys, function ($key, $index) use ($sheet) {
|
||||
$sheet->setCellValue(chr(65 + $index) . ROW, $key);
|
||||
});
|
||||
// Apply the header styles
|
||||
$sheet->getStyle($headers_range)->applyFromArray([
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 15,
|
||||
'name' => 'Indivisa Text Sans',
|
||||
'color' => ['argb' => Color::COLOR_WHITE],
|
||||
],
|
||||
'alignment' => [
|
||||
'horizontal' => Alignment::HORIZONTAL_CENTER,
|
||||
'vertical' => Alignment::VERTICAL_CENTER,
|
||||
],
|
||||
'fill' => [
|
||||
'fillType' => Fill::FILL_SOLID,
|
||||
'startColor' => ['argb' => '001d68'],
|
||||
]
|
||||
]);
|
||||
|
||||
// set filters
|
||||
$sheet->setAutoFilter($headers_range);
|
||||
|
||||
|
||||
|
||||
// Styles that are common for all rows can be set outside the loop
|
||||
|
||||
const DEFAULT_FONT = [
|
||||
'size' => 12,
|
||||
'name' => 'Indivisa Text Sans',
|
||||
'color' => ['argb' => '001d68']
|
||||
];
|
||||
|
||||
const DEFAULT_STYLE = [
|
||||
'alignment' => [
|
||||
'vertical' => Alignment::VERTICAL_CENTER,
|
||||
'wrapText' => true,
|
||||
],
|
||||
'font' => DEFAULT_FONT,
|
||||
'borders' => [
|
||||
'outline' => [
|
||||
'borderStyle' => Border::BORDER_THIN,
|
||||
'color' => ['argb' => Color::COLOR_WHITE],
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($data as $index => $registro) {
|
||||
$pair = $index % 2 == 0;
|
||||
$cellRange = 'A' . (ROW + $index + 1) . ':' . $lastColumnLetter . (ROW + $index + 1);
|
||||
$styleArray = DEFAULT_STYLE;
|
||||
$styleArray['fill'] = [
|
||||
'fillType' => Fill::FILL_SOLID,
|
||||
'startColor' => ['argb' => $pair ? 'd4d9dd' : 'f6f7f8'],
|
||||
];
|
||||
|
||||
$sheet->getStyle($cellRange)->applyFromArray($styleArray);
|
||||
$values = array_values($data_excel);
|
||||
array_walk($values, function ($row, $column_index) use ($sheet, $index, $registro) {
|
||||
$cellLocation = chr(65 + $column_index) . (ROW + $index + 1);
|
||||
$sheet->setCellValue($cellLocation, $registro[$row]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
foreach ($sheet->getColumnIterator() as $column) {
|
||||
$sheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true);
|
||||
}
|
||||
|
||||
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
|
||||
$writer->save('php://output');
|
||||
275
faltas.php
Normal file
275
faltas.php
Normal file
@@ -0,0 +1,275 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Auditoría de faltas</title>
|
||||
<?php
|
||||
include 'import/html_css_files.php';
|
||||
?>
|
||||
<style>
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/jquery-ui.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
|
||||
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="js/bootstrap/bootstrap.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?
|
||||
$redirect = $_SERVER['PHP_SELF'];
|
||||
include "import/html_header.php";
|
||||
global $user;
|
||||
|
||||
html_header(
|
||||
"Faltas",
|
||||
"Sistema de gestión de checador",
|
||||
);
|
||||
|
||||
|
||||
|
||||
if (!$user->periodo_id) { ?>
|
||||
<script defer src="js/jquery.min.js"></script>
|
||||
<script src="js/bootstrap/bootstrap.min.js"></script>
|
||||
|
||||
<div class="modal" id="seleccionar-periodo" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Seleccionar periodo</h2>
|
||||
</div>
|
||||
<div class="modal-body container">
|
||||
<? include 'import/periodo.php' ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$('#seleccionar-periodo').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false,
|
||||
});
|
||||
$('#seleccionar-periodo').modal('show');
|
||||
</script>
|
||||
<? exit;
|
||||
} ?>
|
||||
|
||||
<main class="container-fluid px-4 mt-4" id="app" v-cloak @vue:mounted="mounted" style="min-height: 60vh;"
|
||||
v-scope="">
|
||||
<?php include "import/periodo.php" ?>
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="form-box marco">
|
||||
<? if (!$user->facultad['facultad_id']) { ?>
|
||||
<div class="form-group row">
|
||||
<label for="dlFacultad" class="col-4 col-form-label" id="facultad">Facultad</label>
|
||||
<div class="col-6">
|
||||
<div id="dlFacultad" class="datalist datalist-select mb-1 w-100">
|
||||
<div class="datalist-input">
|
||||
Selecciona una facultad
|
||||
</div>
|
||||
<span class="icono ing-buscar"></span>
|
||||
<ul style="display:none">
|
||||
<li class="datalist-option d-none" data-id="-1">
|
||||
Selecciona una facultad
|
||||
</li>
|
||||
<li class="datalist-option" v-for="facultad in facultades"
|
||||
:key="facultad.facultad_id" :data-id="facultad.facultad_id"
|
||||
@click="filter.facultad = facultad.facultad_id;">
|
||||
(<small> {{facultad.clave_dependencia}} </small>) {{ facultad.facultad_nombre }}
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" id="facultad_id" name="id">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<? } ?>
|
||||
<div class="form-group row">
|
||||
<label for="profesor" class="col-4 col-form-label">Profesor</label>
|
||||
<div class="col-6">
|
||||
<div class="form-row justify-content-around align-items-center">
|
||||
<input id="profesor" name="profesor" class="form-control col-11 mr-1 px-2"
|
||||
placeholder="Seleccione una profesor" list="dlProfesor" v-model="filter.profesor">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm form-control col ml-auto"
|
||||
@click="filter.profesor = '';">
|
||||
<i class="ing-borrar"></i>
|
||||
</button>
|
||||
</div>
|
||||
<datalist id="dlProfesor">
|
||||
<option v-for="profesor in profesores" :key="profesor.profesor_id"
|
||||
:value="`(${profesor.profesor_clave}) ${profesor.profesor_nombre}`">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" v-scope ="{
|
||||
input_faltas: true,
|
||||
}">
|
||||
<label for="porcentaje" class="col-4 col-form-label" id="facultad">
|
||||
<div class="custom-control custom-switch">
|
||||
<span class="mr-5" :class="{'text-muted': input_faltas}">Número</span>
|
||||
<input type="checkbox" class="custom-control-input" id="faltas_porcentaje"
|
||||
v-model="input_faltas" @change="<? if ($user->facultad['facultad_id']) { ?> filter.facultad = <?= $user->facultad['facultad_id'] ?>; <? } ?> filter.porcentaje = input_faltas ? 10 : 0; filter.faltas = input_faltas ? 0 : 1">
|
||||
<label class="custom-control-label" for="faltas_porcentaje"
|
||||
:class="{'text-muted': !input_faltas}">
|
||||
Porcentaje
|
||||
</label>
|
||||
</div>
|
||||
de faltas
|
||||
</label>
|
||||
<div class="col-6">
|
||||
<div id="porcentaje" class="datalist datalist-select mb-1 w-100" V-if="input_faltas">
|
||||
<div class="datalist-input">
|
||||
Selecciona una porcentaje
|
||||
</div>
|
||||
<span class="icono ing-buscar"></span>
|
||||
<ul style="display:none">
|
||||
<li class="datalist-option d-none" data-id="-1">
|
||||
Selecciona un porcentaje
|
||||
</li>
|
||||
<li class="datalist-option"
|
||||
v-for="porcentaje in Array.from({length: 5}, (_, i) => (i + 1) * 10)"
|
||||
:key="facultad.facultad_id" :data-id="facultad.facultad_id"
|
||||
<? if ($user->facultad['facultad_id']) { ?>
|
||||
@click="filter.porcentaje = porcentaje; filter.facultad = <?= $user->facultad['facultad_id'] ?>;"
|
||||
<? } else { ?>
|
||||
@click="filter.porcentaje = porcentaje;"
|
||||
<? } ?> >
|
||||
{{ porcentaje }}%
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" id="facultad_id" name="id">
|
||||
</div>
|
||||
<input type="number" class="form-control" v-model="filter.faltas" v-else
|
||||
@change="" min="1" max="100" step="1"
|
||||
<? if ($user->facultad['facultad_id']) { ?>
|
||||
@click="filter.facultad = <?= $user->facultad['facultad_id'] ?>"
|
||||
<? } ?>
|
||||
placeholder="Número de faltas">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="porcentaje" class="col-4 col-form-label" id="facultad">Faltas</label>
|
||||
<div class="col-6">
|
||||
<div class="form-row justify-content-center align-items-center text-center">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="tipoFaltas"
|
||||
v-model="filter.tipoFaltas" @change="">
|
||||
<label class="custom-control-label" for="tipoFaltas"
|
||||
:class="{'text-muted': !filter.tipoFaltas}">
|
||||
Faltas del {{ filter.tipoFaltas ? 'Supervisor' : 'Profesor' }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row justify-content-center align-items-center">
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-success" @click="toExcel()" :disabled="!faltas.length" :class="{'disabled': !faltas.length}">
|
||||
<i class=" ing-descarga"></i>
|
||||
Exportar a Excel</button>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary" @click="refresh()" :disabled="(filter.facultad <= 0) || (filter.faltas <= 0 && filter.porcentaje <= 0)" :class="{'disabled': !filter.facultad || !filter.faltas && !filter.porcentaje}">
|
||||
<i class=" ing-buscar"></i>
|
||||
Buscar faltas
|
||||
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="table-responsive marco" v-if="faltas.length > 0" v-scope="{orderBy: [
|
||||
{column: 'Profesor', order: 'asc'},
|
||||
{column: 'Faltas', order: 'desc'},
|
||||
{column: 'Total', order: 'desc'},
|
||||
{column: 'Porcentaje', order: 'desc'},
|
||||
]}">
|
||||
<table class="table table-hover table-striped table-bordered table-sm">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th scope="col" class="text-center align-middle px-2" v-for="column in orderBy">
|
||||
{{ column.column }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="falta in faltas.filter(({profesor}) => filter.profesor ? (profesor.profesor_nombre.toLowerCase().includes(filter.profesor.toLowerCase()) || filter.profesor.toLowerCase().includes(`(${profesor.profesor_clave}) ${profesor.profesor_nombre}`.toLowerCase())) : true)"
|
||||
:key="`flata-${falta.profesor_id}`">
|
||||
<td class="align-middle px-2">
|
||||
<strong>{{ falta.profesor.profesor_clave }}</strong>
|
||||
{{ falta.profesor.profesor_nombre }}
|
||||
</td>
|
||||
<td class="align-middle px-2 text-center">
|
||||
{{ falta.faltas }}
|
||||
</td>
|
||||
<td class="align-middle px-2 text-center">
|
||||
{{ falta.total }}
|
||||
</td>
|
||||
<td class="align-middle px-2 text-center">
|
||||
{{ falta.porcentaje }}%
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal" tabindex="-1" id="cargando" data-backdrop="static" data-keyboard="false">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Cargando datos...</h4>
|
||||
|
||||
</div>
|
||||
<div class="modal-body container">
|
||||
<div class="row">
|
||||
<div class="col-12 text-center">
|
||||
<span class="spinner-border spinner-border-lg"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal" tabindex="-1" id="mensaje">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{{mensaje.titulo}}</h4>
|
||||
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body container">
|
||||
<div class="row">
|
||||
<div class="col-12 text-center">
|
||||
{{mensaje.texto}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<!-- <script src=" js/datalist.js"></script> -->
|
||||
<script src="js/datepicker-es.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
|
||||
<script src="js/faltas.js?<?= rand(0, 2) ?>" type="module"></script>
|
||||
<script src="js/scrollables.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
84
js/faltas.js
Normal file
84
js/faltas.js
Normal file
@@ -0,0 +1,84 @@
|
||||
import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
|
||||
const filter = reactive({
|
||||
facultad: -1,
|
||||
profesor: '',
|
||||
porcentaje: 0,
|
||||
faltas: 0,
|
||||
tipoFaltas: true,
|
||||
});
|
||||
|
||||
const app = createApp({
|
||||
filter,
|
||||
facultades: [],
|
||||
profesores: [],
|
||||
faltas: [],
|
||||
mensaje: {
|
||||
titulo: '',
|
||||
texto: '',
|
||||
},
|
||||
async refresh() {
|
||||
if (filter.facultad == -1 || (filter.porcentaje < 10 && filter.faltas < 1)) {
|
||||
console.log('Facultad: ', filter.facultad, 'Porcentaje: ', filter.porcentaje, 'Faltas: ', filter.faltas);
|
||||
return;
|
||||
}
|
||||
$('#cargando').modal('show');
|
||||
try {
|
||||
|
||||
this.faltas = await fetch(`action/profesor_faltas.php?facultad=${this.filter.facultad}&${this.filter.tipoFaltas ? 'supervisor' : 'profesor'}&${this.filter.faltas > 0 ? 'faltas' : 'porcentaje'}=${this.filter.faltas > 0 ? this.filter.faltas : this.filter.porcentaje}`).then(res => res.json());
|
||||
if (this.faltas.error) {
|
||||
$('.modal#mensaje').modal('show');
|
||||
this.mensaje.titulo = 'Información';
|
||||
this.mensaje.texto = this.faltas.error;
|
||||
}
|
||||
} catch (error) {
|
||||
$('.modal#mensaje').modal('show');
|
||||
this.mensaje.titulo = 'Error';
|
||||
this.mensaje.texto = 'No se pudo cargar los datos';
|
||||
}
|
||||
finally {
|
||||
$('#cargando').modal('hide');
|
||||
}
|
||||
},
|
||||
|
||||
async toExcel() {
|
||||
if (filter.facultad == -1 || filter.porcentaje < 10) {
|
||||
return;
|
||||
}
|
||||
$('#cargando').modal('show');
|
||||
try {
|
||||
const response = await fetch(`export/faltas_excel.php`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(this.faltas.map(falta => ({
|
||||
'profesor_clave': falta.profesor.profesor_clave,
|
||||
'profesor_correo': falta.profesor.profesor_correo,
|
||||
'profesor_nombre': falta.profesor.profesor_nombre,
|
||||
'faltas': falta.faltas,
|
||||
'porcentaje': `${falta.porcentaje}%`,
|
||||
'total': falta.total,
|
||||
}))),
|
||||
})
|
||||
|
||||
const blob = await response.blob();
|
||||
window.saveAs(blob, `faltas_${this.facultades.find(facultad => facultad.facultad_id == filter.facultad).facultad_nombre}_${new Date().toISOString().slice(0, 10)}.xlsx`);
|
||||
} catch (error) {
|
||||
$('.modal#mensaje').modal('show');
|
||||
this.mensaje.titulo = 'Error';
|
||||
this.mensaje.texto = 'No se pudo cargar los datos';
|
||||
console.log('Error: ', error);
|
||||
}
|
||||
finally {
|
||||
$('#cargando').modal('hide');
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
try {
|
||||
this.facultades = await fetch('action/action_facultad.php').then(res => res.json());
|
||||
this.profesores = await fetch('action/action_profesor.php').then(res => res.json());
|
||||
} catch (error) {
|
||||
$('.modal#mensaje').modal('show');
|
||||
this.mensaje.titulo = 'Error';
|
||||
this.mensaje.texto = 'No se pudo cargar los datos';
|
||||
console.log('Error: ', error);
|
||||
}
|
||||
}
|
||||
}).mount('#app');
|
||||
38
ts/faltas.ts
Normal file
38
ts/faltas.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
|
||||
// define that $ has type any
|
||||
declare const $: any;
|
||||
|
||||
const filter = reactive({
|
||||
facultad: -1,
|
||||
profesor: '',
|
||||
porcentaje: 0
|
||||
});
|
||||
const app = createApp({
|
||||
filter,
|
||||
facultades: [],
|
||||
profesores: [],
|
||||
|
||||
faltas: [],
|
||||
openModal() {
|
||||
const modal = document.getElementById('cargando');
|
||||
$(modal).modal('show');
|
||||
},
|
||||
closeModal() {
|
||||
const modal = document.getElementById('cargando');
|
||||
$(modal).modal('hide');
|
||||
},
|
||||
|
||||
async refresh() {
|
||||
if(filter.facultad == -1 || filter.porcentaje < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.openModal();
|
||||
this.faltas = await fetch(`action/profesor_faltas.php?facultad=${this.filter.facultad}&profesor=${this.filter.profesor}&porcentaje=${this.filter.porcentaje}`).then(res => res.json());
|
||||
this.closeModal();
|
||||
},
|
||||
async mounted() {
|
||||
this.facultades = await fetch('action/action_facultad.php').then(res => res.json());
|
||||
this.profesores = await fetch('action/action_profesor.php').then(res => res.json());
|
||||
}
|
||||
}).mount('#app');
|
||||
Reference in New Issue
Block a user