first commit
parent
7ee6857696
commit
a1b84a93d6
|
@ -0,0 +1,3 @@
|
|||
FROM postgres:14
|
||||
|
||||
COPY Docker/init_db.sql /docker-entrypoint-initdb.d/
|
|
@ -0,0 +1,3 @@
|
|||
FROM postgres:14
|
||||
|
||||
COPY Docker/init_db_web.sql /docker-entrypoint-initdb.d/
|
|
@ -0,0 +1,27 @@
|
|||
FROM python:3-slim
|
||||
|
||||
# set a directory for the app
|
||||
RUN groupadd app && useradd -m -g app app
|
||||
|
||||
# install dependencies
|
||||
WORKDIR /srv
|
||||
RUN pip3 install setuptools gunicorn
|
||||
COPY front-static/requirements.txt /srv/webinterface/requirements.txt
|
||||
RUN pip3 install --no-cache-dir -r webinterface/requirements.txt
|
||||
|
||||
|
||||
# copy all the files to the container
|
||||
COPY app.py /srv
|
||||
COPY front-static /srv/webinterface
|
||||
COPY static /srv/webinterface/static
|
||||
COPY templates/layout.html templates/home.html templates/objetivos.html /srv/webinterface/templates
|
||||
RUN chown -R app:app /srv
|
||||
|
||||
|
||||
USER app
|
||||
# define the port number the container should expose
|
||||
EXPOSE 8000
|
||||
|
||||
# run the command
|
||||
ENTRYPOINT ["gunicorn"]
|
||||
CMD ["-b", "0.0.0.0:8000", "app:iapp"]
|
|
@ -0,0 +1,25 @@
|
|||
FROM python:3-slim
|
||||
|
||||
# set a directory for the app
|
||||
RUN groupadd app && useradd -m -g app app
|
||||
|
||||
|
||||
# install dependencies
|
||||
WORKDIR /srv
|
||||
RUN pip3 install setuptools gunicorn
|
||||
COPY carga-gtfs/requirements.txt /srv/daemon/requirements.txt
|
||||
RUN pip3 install --no-cache-dir -r daemon/requirements.txt
|
||||
|
||||
|
||||
# copy all the files to the container
|
||||
COPY daemon.py /srv
|
||||
COPY carga-gtfs /srv/daemon
|
||||
|
||||
RUN chown -R app:app /srv
|
||||
|
||||
USER app
|
||||
# define the port number the container should expose
|
||||
EXPOSE 8000
|
||||
|
||||
# run the command
|
||||
CMD ["python3", "daemon.py"]
|
|
@ -0,0 +1,30 @@
|
|||
FROM python:3-slim
|
||||
|
||||
# set a directory for the app
|
||||
RUN groupadd app && useradd -m -g app app
|
||||
|
||||
|
||||
|
||||
# install dependencies
|
||||
WORKDIR /srv
|
||||
RUN pip3 install setuptools gunicorn
|
||||
COPY login-mecanics/requirements.txt /srv/webinterface/requirements.txt
|
||||
RUN pip3 install --no-cache-dir -r webinterface/requirements.txt
|
||||
|
||||
|
||||
# copy all the files to the container
|
||||
COPY app.py /srv
|
||||
COPY login-mecanics /srv/webinterface
|
||||
COPY static /srv/static
|
||||
COPY templates/layout.html /srv/webinterface/templates
|
||||
COPY models/__init__.py models/system.py models/gtfs_work.py models/gtfs_static.py /srv/webinterface/models
|
||||
RUN chown -R app:app /srv
|
||||
|
||||
|
||||
USER app
|
||||
# define the port number the container should expose
|
||||
EXPOSE 8000
|
||||
|
||||
# run the command
|
||||
ENTRYPOINT ["gunicorn"]
|
||||
CMD ["-b", "0.0.0.0:8000", "app:iapp"]
|
|
@ -0,0 +1,28 @@
|
|||
FROM python:3-slim
|
||||
|
||||
# set a directory for the app
|
||||
RUN groupadd app && useradd -m -g app app
|
||||
|
||||
|
||||
|
||||
# install dependencies
|
||||
WORKDIR /srv
|
||||
RUN pip3 install setuptools gunicorn
|
||||
COPY private-dynamic/requirements.txt /srv/webinterface/requirements.txt
|
||||
RUN pip3 install --no-cache-dir -r webinterface/requirements.txt
|
||||
|
||||
|
||||
# copy all the files to the container
|
||||
COPY app.py /srv
|
||||
COPY private-dynamic /srv/webinterface
|
||||
COPY models/__init__.py models/system.py models/gtfs_work.py models/gtfs_static.py /srv/webinterface/models
|
||||
RUN chown -R app:app /srv
|
||||
|
||||
|
||||
USER app
|
||||
# define the port number the container should expose
|
||||
EXPOSE 8000
|
||||
|
||||
# run the command
|
||||
ENTRYPOINT ["gunicorn"]
|
||||
CMD ["-b", "0.0.0.0:8000", "app:iapp"]
|
|
@ -0,0 +1,30 @@
|
|||
FROM python:3-slim
|
||||
|
||||
# set a directory for the app
|
||||
RUN groupadd app && useradd -m -g app app
|
||||
|
||||
|
||||
|
||||
# install dependencies
|
||||
WORKDIR /srv
|
||||
RUN pip3 install setuptools gunicorn
|
||||
COPY public-dynamic/requirements.txt /srv/webinterface/requirements.txt
|
||||
RUN pip3 install --no-cache-dir -r webinterface/requirements.txt
|
||||
|
||||
|
||||
# copy all the files to the container
|
||||
COPY app.py /srv
|
||||
COPY public-dynamic /srv/webinterface
|
||||
COPY static /srv/static
|
||||
COPY templates/layout.html templates/home.html templates/objetivos.html /srv/webinterface/templates
|
||||
COPY models/__init__.py models/system.py models/gtfs_work.py models/gtfs_static.py /srv/webinterface/models
|
||||
RUN chown -R app:app /srv
|
||||
|
||||
|
||||
USER app
|
||||
# define the port number the container should expose
|
||||
EXPOSE 8000
|
||||
|
||||
# run the command
|
||||
ENTRYPOINT ["gunicorn"]
|
||||
CMD ["-b", "0.0.0.0:8000", "app:iapp"]
|
|
@ -0,0 +1,4 @@
|
|||
CREATE USER docker superuser login password 'docker';
|
||||
|
||||
CREATE DATABASE docker;
|
||||
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
|
|
@ -0,0 +1,915 @@
|
|||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
CREATE USER docker superuser login password 'docker';
|
||||
|
||||
CREATE DATABASE docker;
|
||||
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
|
||||
|
||||
-- Dumped from database version 14.6 (Debian 14.6-1.pgdg110+1)
|
||||
-- Dumped by pg_dump version 14.6 (Ubuntu 14.6-0ubuntu0.22.04.1)
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET idle_in_transaction_session_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
--
|
||||
-- Name: registros; Type: SCHEMA; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE SCHEMA registros;
|
||||
|
||||
|
||||
--
|
||||
-- Name: usuarios; Type: SCHEMA; Schema: -; Owner: -
|
||||
--
|
||||
|
||||
CREATE SCHEMA usuarios;
|
||||
|
||||
|
||||
SET default_tablespace = '';
|
||||
|
||||
SET default_table_access_method = heap;
|
||||
|
||||
--
|
||||
-- Name: conexiones; Type: TABLE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE registros.conexiones (
|
||||
id bigint NOT NULL,
|
||||
ipaddrid integer NOT NULL,
|
||||
sesionid integer NOT NULL,
|
||||
iniciada timestamp with time zone DEFAULT now(),
|
||||
ultimo timestamp with time zone
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: conexiones_id_seq; Type: SEQUENCE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE registros.conexiones_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: conexiones_id_seq; Type: SEQUENCE OWNED BY; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE registros.conexiones_id_seq OWNED BY registros.conexiones.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: dispositivos; Type: TABLE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE registros.dispositivos (
|
||||
id integer NOT NULL,
|
||||
useragent character varying(1024) NOT NULL,
|
||||
parsed_ua character varying(150),
|
||||
dev character varying(50),
|
||||
os character varying(50),
|
||||
browser character varying(50)
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: dispositivos_id_seq; Type: SEQUENCE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE registros.dispositivos_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: dispositivos_id_seq; Type: SEQUENCE OWNED BY; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE registros.dispositivos_id_seq OWNED BY registros.dispositivos.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: ipaddrs; Type: TABLE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE registros.ipaddrs (
|
||||
id integer NOT NULL,
|
||||
ipaddr inet NOT NULL,
|
||||
hostname character varying(100)
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ipaddrs_id_seq; Type: SEQUENCE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE registros.ipaddrs_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: ipaddrs_id_seq; Type: SEQUENCE OWNED BY; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE registros.ipaddrs_id_seq OWNED BY registros.ipaddrs.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: registros; Type: TABLE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE registros.registros (
|
||||
id bigint NOT NULL,
|
||||
sitioid integer NOT NULL,
|
||||
rutaid integer NOT NULL,
|
||||
sesionid bigint NOT NULL,
|
||||
ipaddrid integer NOT NULL,
|
||||
tamano integer,
|
||||
creado timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: registros_id_seq; Type: SEQUENCE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE registros.registros_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: registros_id_seq; Type: SEQUENCE OWNED BY; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE registros.registros_id_seq OWNED BY registros.registros.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: rutas; Type: TABLE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE registros.rutas (
|
||||
id integer NOT NULL,
|
||||
ruta character varying(100) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: rutas_id_seq; Type: SEQUENCE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE registros.rutas_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: rutas_id_seq; Type: SEQUENCE OWNED BY; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE registros.rutas_id_seq OWNED BY registros.rutas.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: sesiones; Type: TABLE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE registros.sesiones (
|
||||
id bigint NOT NULL,
|
||||
identidadid bigint NOT NULL,
|
||||
dispositivoid integer NOT NULL,
|
||||
iniciada timestamp with time zone DEFAULT now(),
|
||||
ultimo timestamp with time zone
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sesiones_id_seq; Type: SEQUENCE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE registros.sesiones_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: sesiones_id_seq; Type: SEQUENCE OWNED BY; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE registros.sesiones_id_seq OWNED BY registros.sesiones.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: sitios; Type: TABLE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE registros.sitios (
|
||||
id integer NOT NULL,
|
||||
sitio character varying(100) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sitios_id_seq; Type: SEQUENCE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE registros.sitios_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: sitios_id_seq; Type: SEQUENCE OWNED BY; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE registros.sitios_id_seq OWNED BY registros.sitios.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: ubicaciones; Type: TABLE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE registros.ubicaciones (
|
||||
id bigint NOT NULL,
|
||||
ipaddrid integer NOT NULL,
|
||||
identidadid integer NOT NULL,
|
||||
descripcion character varying(200),
|
||||
iniciada timestamp with time zone DEFAULT now(),
|
||||
ultimo timestamp with time zone
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ubicaciones_id_seq; Type: SEQUENCE; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE registros.ubicaciones_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: ubicaciones_id_seq; Type: SEQUENCE OWNED BY; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE registros.ubicaciones_id_seq OWNED BY registros.ubicaciones.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: correos; Type: TABLE; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE usuarios.correos (
|
||||
id integer NOT NULL,
|
||||
correo character varying(100) NOT NULL,
|
||||
cuentaid bigint NOT NULL,
|
||||
"default" boolean
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: correos_id_seq; Type: SEQUENCE; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE usuarios.correos_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: correos_id_seq; Type: SEQUENCE OWNED BY; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE usuarios.correos_id_seq OWNED BY usuarios.correos.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: cuentas; Type: TABLE; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE usuarios.cuentas (
|
||||
id bigint NOT NULL,
|
||||
clave character varying(60) NOT NULL,
|
||||
sysadmin boolean,
|
||||
ultimoacceso timestamp with time zone
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: identidades; Type: TABLE; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE usuarios.identidades (
|
||||
id bigint NOT NULL,
|
||||
login character varying(50) NOT NULL,
|
||||
alias character varying(50),
|
||||
creado timestamp with time zone DEFAULT now(),
|
||||
modificado timestamp with time zone,
|
||||
tipo character varying(20)
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: identidades_id_seq; Type: SEQUENCE; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE usuarios.identidades_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: identidades_id_seq; Type: SEQUENCE OWNED BY; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE usuarios.identidades_id_seq OWNED BY usuarios.identidades.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: personas; Type: TABLE; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE usuarios.personas (
|
||||
id bigint NOT NULL,
|
||||
nombres character varying(100) NOT NULL,
|
||||
apellidop character varying(100) NOT NULL,
|
||||
apellidom character varying(100),
|
||||
rut character varying(20) NOT NULL,
|
||||
telefono character varying(20),
|
||||
foto character varying(50) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: conexiones id; Type: DEFAULT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.conexiones ALTER COLUMN id SET DEFAULT nextval('registros.conexiones_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: dispositivos id; Type: DEFAULT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.dispositivos ALTER COLUMN id SET DEFAULT nextval('registros.dispositivos_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ipaddrs id; Type: DEFAULT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.ipaddrs ALTER COLUMN id SET DEFAULT nextval('registros.ipaddrs_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: registros id; Type: DEFAULT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.registros ALTER COLUMN id SET DEFAULT nextval('registros.registros_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: rutas id; Type: DEFAULT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.rutas ALTER COLUMN id SET DEFAULT nextval('registros.rutas_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sesiones id; Type: DEFAULT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.sesiones ALTER COLUMN id SET DEFAULT nextval('registros.sesiones_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sitios id; Type: DEFAULT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.sitios ALTER COLUMN id SET DEFAULT nextval('registros.sitios_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ubicaciones id; Type: DEFAULT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.ubicaciones ALTER COLUMN id SET DEFAULT nextval('registros.ubicaciones_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: correos id; Type: DEFAULT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.correos ALTER COLUMN id SET DEFAULT nextval('usuarios.correos_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: identidades id; Type: DEFAULT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.identidades ALTER COLUMN id SET DEFAULT nextval('usuarios.identidades_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: conexiones; Type: TABLE DATA; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
COPY registros.conexiones (id, ipaddrid, sesionid, iniciada, ultimo) FROM stdin;
|
||||
1 1 1 2023-02-27 01:18:31.243924+00 2023-02-27 01:18:31.24764+00
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: dispositivos; Type: TABLE DATA; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
COPY registros.dispositivos (id, useragent, parsed_ua, dev, os, browser) FROM stdin;
|
||||
1 Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0 PC / Linux / Firefox 110.0 PC Linux Firefox 110.0
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: ipaddrs; Type: TABLE DATA; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
COPY registros.ipaddrs (id, ipaddr, hostname) FROM stdin;
|
||||
1 172.23.0.1 \N
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: registros; Type: TABLE DATA; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
COPY registros.registros (id, sitioid, rutaid, sesionid, ipaddrid, tamano, creado) FROM stdin;
|
||||
1 1 1 1 1 0 2023-02-27 01:18:31.248091
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: rutas; Type: TABLE DATA; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
COPY registros.rutas (id, ruta) FROM stdin;
|
||||
1 /favicon.ico
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: sesiones; Type: TABLE DATA; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
COPY registros.sesiones (id, identidadid, dispositivoid, iniciada, ultimo) FROM stdin;
|
||||
1 2 1 2023-02-27 01:18:31.241528+00 2023-02-27 01:18:31.24764+00
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: sitios; Type: TABLE DATA; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
COPY registros.sitios (id, sitio) FROM stdin;
|
||||
1 localhost:5003
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: ubicaciones; Type: TABLE DATA; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
COPY registros.ubicaciones (id, ipaddrid, identidadid, descripcion, iniciada, ultimo) FROM stdin;
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: correos; Type: TABLE DATA; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
COPY usuarios.correos (id, correo, cuentaid, "default") FROM stdin;
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: cuentas; Type: TABLE DATA; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
COPY usuarios.cuentas (id, clave, sysadmin, ultimoacceso) FROM stdin;
|
||||
4 $2b$12$Xa.mKurU3IVc54Pirzs9ReAjI4AivIFu/EGUelx59/xorVN1Jkb5i f \N
|
||||
5 $2b$12$2f6Zjld4.4lMz77r9gncNe4O2nDNVs1lV0CkpbRDOW3b8876h3wyC f \N
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: identidades; Type: TABLE DATA; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
COPY usuarios.identidades (id, login, alias, creado, modificado, tipo) FROM stdin;
|
||||
1 Internet \N 2023-02-27 01:18:31.225317+00 \N Identidad
|
||||
2 Intranet \N 2023-02-27 01:18:31.225317+00 \N Identidad
|
||||
3 Buscador \N 2023-02-27 01:18:31.225317+00 \N Identidad
|
||||
4 ifiguero \N 2023-02-27 01:18:31.225317+00 \N Persona
|
||||
5 tpmc \N 2023-02-27 01:18:31.225317+00 \N Persona
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: personas; Type: TABLE DATA; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
COPY usuarios.personas (id, nombres, apellidop, apellidom, rut, telefono, foto) FROM stdin;
|
||||
4 Israel Figueroa \N 13955977-0 \N default.jpg
|
||||
5 Transporte Público \N tpmc \N default.jpg
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Name: conexiones_id_seq; Type: SEQUENCE SET; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('registros.conexiones_id_seq', 1, true);
|
||||
|
||||
|
||||
--
|
||||
-- Name: dispositivos_id_seq; Type: SEQUENCE SET; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('registros.dispositivos_id_seq', 1, true);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ipaddrs_id_seq; Type: SEQUENCE SET; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('registros.ipaddrs_id_seq', 1, true);
|
||||
|
||||
|
||||
--
|
||||
-- Name: registros_id_seq; Type: SEQUENCE SET; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('registros.registros_id_seq', 1, true);
|
||||
|
||||
|
||||
--
|
||||
-- Name: rutas_id_seq; Type: SEQUENCE SET; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('registros.rutas_id_seq', 1, true);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sesiones_id_seq; Type: SEQUENCE SET; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('registros.sesiones_id_seq', 1, true);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sitios_id_seq; Type: SEQUENCE SET; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('registros.sitios_id_seq', 1, true);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ubicaciones_id_seq; Type: SEQUENCE SET; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('registros.ubicaciones_id_seq', 1, false);
|
||||
|
||||
|
||||
--
|
||||
-- Name: correos_id_seq; Type: SEQUENCE SET; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('usuarios.correos_id_seq', 1, false);
|
||||
|
||||
|
||||
--
|
||||
-- Name: identidades_id_seq; Type: SEQUENCE SET; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('usuarios.identidades_id_seq', 5, true);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ubicaciones _una_ip_identidad_uc; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.ubicaciones
|
||||
ADD CONSTRAINT _una_ip_identidad_uc UNIQUE (ipaddrid, identidadid);
|
||||
|
||||
|
||||
--
|
||||
-- Name: conexiones _una_ip_sesion_uc; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.conexiones
|
||||
ADD CONSTRAINT _una_ip_sesion_uc UNIQUE (ipaddrid, sesionid);
|
||||
|
||||
|
||||
--
|
||||
-- Name: conexiones conexiones_pkey; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.conexiones
|
||||
ADD CONSTRAINT conexiones_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: dispositivos dispositivos_pkey; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.dispositivos
|
||||
ADD CONSTRAINT dispositivos_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: dispositivos dispositivos_useragent_key; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.dispositivos
|
||||
ADD CONSTRAINT dispositivos_useragent_key UNIQUE (useragent);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ipaddrs ipaddrs_ipaddr_key; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.ipaddrs
|
||||
ADD CONSTRAINT ipaddrs_ipaddr_key UNIQUE (ipaddr);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ipaddrs ipaddrs_pkey; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.ipaddrs
|
||||
ADD CONSTRAINT ipaddrs_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: registros registros_pkey; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.registros
|
||||
ADD CONSTRAINT registros_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: rutas rutas_pkey; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.rutas
|
||||
ADD CONSTRAINT rutas_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: rutas rutas_ruta_key; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.rutas
|
||||
ADD CONSTRAINT rutas_ruta_key UNIQUE (ruta);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sesiones sesiones_pkey; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.sesiones
|
||||
ADD CONSTRAINT sesiones_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sitios sitios_pkey; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.sitios
|
||||
ADD CONSTRAINT sitios_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sitios sitios_sitio_key; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.sitios
|
||||
ADD CONSTRAINT sitios_sitio_key UNIQUE (sitio);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ubicaciones ubicaciones_pkey; Type: CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.ubicaciones
|
||||
ADD CONSTRAINT ubicaciones_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: correos correos_correo_key; Type: CONSTRAINT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.correos
|
||||
ADD CONSTRAINT correos_correo_key UNIQUE (correo);
|
||||
|
||||
|
||||
--
|
||||
-- Name: correos correos_pkey; Type: CONSTRAINT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.correos
|
||||
ADD CONSTRAINT correos_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: cuentas cuentas_pkey; Type: CONSTRAINT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.cuentas
|
||||
ADD CONSTRAINT cuentas_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: identidades identidades_login_key; Type: CONSTRAINT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.identidades
|
||||
ADD CONSTRAINT identidades_login_key UNIQUE (login);
|
||||
|
||||
|
||||
--
|
||||
-- Name: identidades identidades_pkey; Type: CONSTRAINT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.identidades
|
||||
ADD CONSTRAINT identidades_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: personas personas_pkey; Type: CONSTRAINT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.personas
|
||||
ADD CONSTRAINT personas_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: personas personas_rut_key; Type: CONSTRAINT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.personas
|
||||
ADD CONSTRAINT personas_rut_key UNIQUE (rut);
|
||||
|
||||
|
||||
--
|
||||
-- Name: conexiones conexiones_ipaddrid_fkey; Type: FK CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.conexiones
|
||||
ADD CONSTRAINT conexiones_ipaddrid_fkey FOREIGN KEY (ipaddrid) REFERENCES registros.ipaddrs(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: conexiones conexiones_sesionid_fkey; Type: FK CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.conexiones
|
||||
ADD CONSTRAINT conexiones_sesionid_fkey FOREIGN KEY (sesionid) REFERENCES registros.sesiones(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: registros registros_ipaddrid_fkey; Type: FK CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.registros
|
||||
ADD CONSTRAINT registros_ipaddrid_fkey FOREIGN KEY (ipaddrid) REFERENCES registros.ipaddrs(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: registros registros_rutaid_fkey; Type: FK CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.registros
|
||||
ADD CONSTRAINT registros_rutaid_fkey FOREIGN KEY (rutaid) REFERENCES registros.rutas(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: registros registros_sesionid_fkey; Type: FK CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.registros
|
||||
ADD CONSTRAINT registros_sesionid_fkey FOREIGN KEY (sesionid) REFERENCES registros.sesiones(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: registros registros_sitioid_fkey; Type: FK CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.registros
|
||||
ADD CONSTRAINT registros_sitioid_fkey FOREIGN KEY (sitioid) REFERENCES registros.sitios(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sesiones sesiones_dispositivoid_fkey; Type: FK CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.sesiones
|
||||
ADD CONSTRAINT sesiones_dispositivoid_fkey FOREIGN KEY (dispositivoid) REFERENCES registros.dispositivos(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sesiones sesiones_identidadid_fkey; Type: FK CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.sesiones
|
||||
ADD CONSTRAINT sesiones_identidadid_fkey FOREIGN KEY (identidadid) REFERENCES usuarios.identidades(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ubicaciones ubicaciones_identidadid_fkey; Type: FK CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.ubicaciones
|
||||
ADD CONSTRAINT ubicaciones_identidadid_fkey FOREIGN KEY (identidadid) REFERENCES usuarios.identidades(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ubicaciones ubicaciones_ipaddrid_fkey; Type: FK CONSTRAINT; Schema: registros; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY registros.ubicaciones
|
||||
ADD CONSTRAINT ubicaciones_ipaddrid_fkey FOREIGN KEY (ipaddrid) REFERENCES registros.ipaddrs(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: correos correos_cuentaid_fkey; Type: FK CONSTRAINT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.correos
|
||||
ADD CONSTRAINT correos_cuentaid_fkey FOREIGN KEY (cuentaid) REFERENCES usuarios.cuentas(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: cuentas cuentas_id_fkey; Type: FK CONSTRAINT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.cuentas
|
||||
ADD CONSTRAINT cuentas_id_fkey FOREIGN KEY (id) REFERENCES usuarios.identidades(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: personas personas_id_fkey; Type: FK CONSTRAINT; Schema: usuarios; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY usuarios.personas
|
||||
ADD CONSTRAINT personas_id_fkey FOREIGN KEY (id) REFERENCES usuarios.cuentas(id);
|
||||
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
|
@ -0,0 +1,6 @@
|
|||
from webinterface import create_app
|
||||
|
||||
iapp = create_app()
|
||||
|
||||
if __name__ == '__main__':
|
||||
iapp.run()
|
|
@ -0,0 +1,6 @@
|
|||
import logging
|
||||
|
||||
log = logging.getLogger()
|
||||
logging.getLogger("carga_gtfs").setLevel(
|
||||
logging.INFO
|
||||
) # Prevent debug messages from peewee lib
|
|
@ -0,0 +1,147 @@
|
|||
import os
|
||||
import io
|
||||
import csv
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
def parse_time(strinput):
|
||||
try:
|
||||
h, m, s = strinput.split(":")
|
||||
h = int(h)
|
||||
d = 0
|
||||
while h > 23:
|
||||
d += 1
|
||||
h -= 24
|
||||
|
||||
return datetime.strptime("{}:{}:{}".format(h,m,s), '%H:%M:%S').time(), d
|
||||
except:
|
||||
current_app.logger.debug('Traceback {}'.format(traceback.format_exc()))
|
||||
current_app.logger.warning("Hora incorrecta => {}".format(strinput))
|
||||
return datetime.strptime("01:01:01", '%H:%M:%S').time(), 0
|
||||
|
||||
|
||||
def parse_paraderoid(strinput):
|
||||
primerodenero2022 = 44562
|
||||
|
||||
if strinput.find("-") > 0:
|
||||
current_app.logger.error("Error en el dato de la parada: {}".format(strinput))
|
||||
try:
|
||||
whatdate= datetime.strptime('{}-{}'.format(strinput, 2022), '%d-%b-%Y').date()
|
||||
idparadero = primerodenero2022 + whatdate.timetuple().tm_yday
|
||||
current_app.logger.warning("Calculado el Identificador en base al año 2022 => {}".format(idparadero))
|
||||
return idparadero
|
||||
except:
|
||||
return 0
|
||||
else:
|
||||
return int(strinput)
|
||||
|
||||
|
||||
def completa_dataset():
|
||||
iparaderos = 0
|
||||
for paradero in Paradero.query.all():
|
||||
iparaderos += 1
|
||||
cparadero = 0
|
||||
lparadas = 0
|
||||
sparadas = 0
|
||||
dparadas = 0
|
||||
lineas = []
|
||||
lparada = []
|
||||
|
||||
for parada in Parada.query.filter(Parada.paraderoid==paradero.id).all():
|
||||
cparadero += 1
|
||||
if cparadero % 1000 == 0:
|
||||
current_app.logger.debug("Paradero {}, {} detenciones. {} lineas".format(iparaderos, cparadero, len(lineas)))
|
||||
|
||||
elemento = (parada.servicio.lineaid, parada.servicio.direccionid)
|
||||
|
||||
if parada.servicio.dotw == 'S':
|
||||
sparadas += 1
|
||||
elif parada.servicio.dotw == 'D':
|
||||
dparadas += 1
|
||||
else:
|
||||
lparadas += 1
|
||||
|
||||
if elemento in lineas:
|
||||
continue
|
||||
|
||||
lineas.append(elemento)
|
||||
if parada.servicio.direccionid:
|
||||
lparada.append("{} ({} ida)".format(parada.servicio.linea.codigo, parada.secuencia))
|
||||
else:
|
||||
lparada.append("{} ({} regreso)".format(parada.servicio.linea.codigo, parada.secuencia))
|
||||
|
||||
lparada.sort()
|
||||
paradero.recorridos = " - ".join(lparada)
|
||||
paradero.paradas = 5*lparadas+sparadas+dparadas
|
||||
paradero.resumen = "{} paradas a la semana, {} lineas".format(paradero.paradas, len(lineas))
|
||||
paradero.paradasl = lparadas
|
||||
paradero.paradass = sparadas
|
||||
paradero.paradasd = dparadas
|
||||
paradero.lineas = len(lineas)
|
||||
current_app.logger.info("Paradero {}, {} detenciones a la semana en {} lineas".format(iparaderos, paradero.paradas, len(lineas)))
|
||||
if iparaderos % 10 == 0:
|
||||
db.session.commit()
|
||||
|
||||
db.session.commit()
|
||||
|
||||
def agrega_paraderos():
|
||||
from bs4 import BeautifulSoup
|
||||
from zipfile import ZipFile
|
||||
|
||||
# print(os.getcwd())
|
||||
|
||||
current_app.logger.info("Archivo de paraderos, kmz")
|
||||
with ZipFile('webinterface/models/datos/PARADEROS_FORMALES.kmz', 'r') as kmz:
|
||||
kml = kmz.open(kmz.filelist[0].filename, 'r').read()
|
||||
|
||||
soup = BeautifulSoup(kml, 'xml')
|
||||
existen = 0
|
||||
nuevo = 0
|
||||
|
||||
|
||||
for paradero in soup.find_all("kml:Placemark"):
|
||||
if (existen+nuevo) % 1000 == 0:
|
||||
current_app.logger.debug("Leidos {} paraderos existentes y {} nuevos".format(existen, nuevo))
|
||||
|
||||
|
||||
lat = int(paradero.find("kml:SimpleData", {"name": "LATITUDE"}).text)/1000000
|
||||
lng = int(paradero.find("kml:SimpleData", {"name": "LONGITUDE"}).text)/1000000
|
||||
|
||||
codigo = paradero.find("kml:SimpleData", {"name": "CODIGO_INT"}).text
|
||||
comuna = paradero.find("kml:SimpleData", {"name": "COMUNA"}).text
|
||||
recorridos = paradero.find("kml:SimpleData", {"name": "RECORRIDOS"}).text
|
||||
tamano = paradero.find("kml:SimpleData", {"name": "SUPERFICIE"}).text
|
||||
capacidad = paradero.find("kml:SimpleData", {"name": "CAPACIDAD"}).text
|
||||
materialpiso = paradero.find("kml:SimpleData", {"name": "RADIER_MAT"}).text
|
||||
materialpared = paradero.find("kml:SimpleData", {"name": "PARED_POS2"}).text
|
||||
materialtecho = paradero.find("kml:SimpleData", {"name": "TECHUMBRE2"}).text
|
||||
|
||||
|
||||
match = None
|
||||
|
||||
for radio in [0.00000001, 0.00000002, 0.00000003, 0.00000004, 0.00000005, 0.000000075, 0.0000001, 0.0000002]:
|
||||
match = Paradero.query.filter(((Paradero.latitud-lat)*(Paradero.latitud-lat)+(Paradero.longitud-lng)*(Paradero.longitud-lng)) < radio).first()
|
||||
|
||||
if match is not None:
|
||||
break
|
||||
|
||||
if match is None:
|
||||
nuevo += 1
|
||||
nombre = "{} cerca {}".format(paradero.find("kml:SimpleData", {"name": "NOMBRE_DE_"}).text, paradero.find("kml:SimpleData", {"name": "CALLE_CERC"}).text)
|
||||
match = Paradero(id=codigo, codigo=codigo, nombre=nombre, latitud=lat, longitud=lng)
|
||||
db.session.add(match)
|
||||
db.session.commit()
|
||||
else:
|
||||
existen += 1
|
||||
|
||||
match.codigo = codigo
|
||||
match.comuna = comuna
|
||||
match.recorridos2 = recorridos
|
||||
match.tamano = tamano
|
||||
match.capacidad = capacidad
|
||||
match.techo = materialtecho
|
||||
match.pared = materialpared
|
||||
match.piso = materialpiso
|
||||
db.session.commit()
|
||||
|
||||
current_app.logger.info("Cargados {} paraderos existentes y {} nuevos".format(existen, nuevo))
|
|
@ -0,0 +1,172 @@
|
|||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from webinterface import db
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
Model = declarative_base()
|
||||
engine = create_engine(os.environ.get('SQLALCHEMY_GTFSDB_URI'), echo=False)
|
||||
Session = sessionmaker(bind=engine, autocommit=False, autoflush=True)
|
||||
session = Session()
|
||||
|
||||
|
||||
|
||||
class sArchivosGTFS(Model):
|
||||
__tablename__ = 'gtfs'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
descripcion = Column(String)
|
||||
file = Column(String)
|
||||
hash = Column(String)
|
||||
size = Column(Integer)
|
||||
loaded = Column(Integer, default=0)
|
||||
ts = Column(Datetime, default=func.now())
|
||||
|
||||
#agency_id,agency_name,agency_url,agency_timezone,agency_lang,agency_phone,agency_fare_url
|
||||
#DTPR,División de Transporte Público Regional,http://www.dtpr.gob.cl/,America/Santiago,es,+562 2421 3580,
|
||||
class sAgencia(Model):
|
||||
__tablename__ = 'agency'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
agency_id = Column(String)
|
||||
agency_name = Column(String)
|
||||
agency_url = Column(String)
|
||||
agency_timezone = Column(String)
|
||||
agency_lang = Column(String)
|
||||
agency_phone = Column(String)
|
||||
agency_fare_url = Column(String)
|
||||
|
||||
#service_id,start_date,end_date,monday,tuesday,wednesday,thursday,friday,saturday,sunday
|
||||
#S,20210101,20311231,0,0,0,0,0,1,0
|
||||
#D,20210101,20311231,0,0,0,0,0,0,1
|
||||
#L,20210101,20311231,1,1,1,1,1,0,0
|
||||
class sCalendario(Model):
|
||||
__tablename__ = 'calendar'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
service_id = Column(String)
|
||||
start_date = Column(String)
|
||||
end_date = Column(String)
|
||||
monday = Column(Integer)
|
||||
tuesday = Column(Integer)
|
||||
wednesday = Column(Integer)
|
||||
thursday = Column(Integer)
|
||||
friday = Column(Integer)
|
||||
saturday = Column(Integer)
|
||||
sunday = Column(Integer)
|
||||
|
||||
#feed_publisher_name,feed_publisher_url,feed_lang,feed_start_date,feed_end_date,feed_version
|
||||
#División de Transporte Público Regional,http://www.dtpr.gob.cl/,es,20210101,20311231,Gran Concepción20220616
|
||||
class sFeedInfo(Model):
|
||||
__tablename__ = 'feed_info'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
feed_publisher_name = Column(String)
|
||||
feed_publisher_url = Column(String)
|
||||
feed_lang = Column(String)
|
||||
feed_start_date = Column(String)
|
||||
feed_end_date = Column(String)
|
||||
feed_version = Column(String)
|
||||
|
||||
#route_id,agency_id,route_short_name,route_long_name,route_desc,route_type,route_url,route_color,route_text_color
|
||||
#625,DTPR,70KH,Nonguén - Parque Tumbes,,3,,0d7215,ffffff
|
||||
#600,DTPR,41CR,Parque Empresarial Biobío - Terminal Collao,,3,,ad0101,ffffff
|
||||
class sRuta(Model):
|
||||
__tablename__ = 'routes'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
route_id = Column(String)
|
||||
agency_id = Column(String)
|
||||
route_short_name = Column(String)
|
||||
route_long_name = Column(String)
|
||||
route_desc = Column(String)
|
||||
route_type = Column(Integer)
|
||||
route_url = Column(String)
|
||||
route_color = Column(String)
|
||||
route_text_color = Column(String)
|
||||
|
||||
#shape_id,shape_pt_lat,shape_pt_lon,shape_pt_sequence,shape_dist_traveled
|
||||
#1136979693,-36.843,-73.00984,1
|
||||
#1136979693,-36.843,-73.00984,2
|
||||
class sFormas(Model):
|
||||
__tablename__ = 'shapes'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
shape_id = Column(String)
|
||||
shape_pt_lat = Column(Double)
|
||||
shape_pt_lon = Column(Double)
|
||||
shape_pt_sequence = Column(Integer)
|
||||
shape_dist_traveled = Column(Double)
|
||||
|
||||
#stop_id,stop_code,stop_name,stop_desc,stop_lat,stop_lon,zone_id,stop_url,location_type,parent_station,wheelchair_boarding
|
||||
#40921,,Pque Zoologico Concepcion Poniente,,-36.8400453,-73.00696914,,,,,
|
||||
#40808,,Cno. Nonguen esq Las Vertientes,,-36.83675878,-73.00343935,,,,,
|
||||
class sParadas(Model):
|
||||
__tablename__ = 'stops'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
stop_id = Column(String)
|
||||
stop_code = Column(String)
|
||||
stop_name = Column(String)
|
||||
stop_desc = Column(String)
|
||||
stop_lat = Column(Double)
|
||||
stop_lon = Column(Double)
|
||||
zone_id = Column(String)
|
||||
stop_url = Column(String)
|
||||
location_type = Column(Integer)
|
||||
parent_station = Column(String)
|
||||
wheelchair_boarding = Column(Integer)
|
||||
#trip_id,arrival_time,departure_time,stop_id,stop_sequence, timepoint,shape_dist_traveled
|
||||
#trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type,timepoint
|
||||
#c8b17d5f-4-76aabf89-b,05:01:00,05:01:00,40439,1,,,,1
|
||||
#c8b17d5f-4-76aabf89-b,05:02:00,05:02:00,40440,2,,,,0
|
||||
class sDetenciones(Model):
|
||||
__tablename__ = 'stop_times'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
trip_id = Column(String)
|
||||
arrival_time = Column(String)
|
||||
departure_time = Column(String)
|
||||
stop_id = Column(String)
|
||||
stop_sequence = Column(Integer)
|
||||
stop_headsign = Column(String)
|
||||
pickup_type = Column(Integer)
|
||||
drop_off_type = Column(Integer)
|
||||
timepoint = Column(Integer)
|
||||
#,,,,,,
|
||||
|
||||
#route_id,service_id,trip_id,trip_headsign, direction_id,block_id,shape_id,wheelchair_accessible,bikes_allowed
|
||||
#route_id,service_id,trip_id,trip_headsign,trip_short_name,direction_id,block_id,shape_id,wheelchair_accessible,bikes_allowed
|
||||
#625,S,c8b17d5f-4-76aabf89-b,Nonguén,,1,,1136979694,,
|
||||
#625,S,4d018d35-7-76aabf89-b,Parque Tumbes,,0,,1136979693,,
|
||||
#ac48288b-1-56195eaf-8,b1910f5b,1,0,1168259538,,0,0,1-1,1
|
||||
#d9e94be9-f-56195eaf-8,b1910f5b,1,0,1168259538,,0,0,1-1,2
|
||||
class sServicio(Model):
|
||||
__tablename__ = 'trips'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
|
||||
id = Column(String, primary_key=True, nullable=False)
|
||||
|
||||
route_id = Column(String) # Ruta.rutaid
|
||||
service_id = Column(String)
|
||||
trip_id = Column(String)
|
||||
trip_headsign = Column(String)
|
||||
trip_short_name = Column(String)
|
||||
direction_id = Column(Integer)
|
||||
block_id = Column(String)
|
||||
shape_id = Column(String)
|
||||
wheelchair_accessible = Column(Integer)
|
||||
bikes_allowed = Column(Integer)
|
||||
|
||||
# 4cc08782-c-76aabf89-b
|
|
@ -0,0 +1,176 @@
|
|||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from sqlalchemy import Integer, BigInteger, Double, Float, String, Time, Column, ForeignKey, UniqueConstraint
|
||||
|
||||
|
||||
Model = declarative_base()
|
||||
engine = create_engine(os.environ.get('SQLALCHEMY_HOTDB_URI'), echo=False)
|
||||
Session = sessionmaker(bind=engine, autocommit=False, autoflush=True)
|
||||
session = Session()
|
||||
|
||||
|
||||
class ArchivosGTFS(Model):
|
||||
__tablename__ = 'archivos'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
|
||||
analisis = Column(Integer, default=0)
|
||||
terminado = Column(Integer, default=0)
|
||||
activo = Column(Integer, default=0)
|
||||
|
||||
class Agencia(Model):
|
||||
__tablename__ = 'agency'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
agency_id = Column(String, unique=True)
|
||||
agency_name = Column(String)
|
||||
agency_url = Column(String)
|
||||
agency_timezone = Column(String)
|
||||
agency_lang = Column(String)
|
||||
agency_phone = Column(String)
|
||||
agency_fare_url = Column(String)
|
||||
|
||||
class Calendario(Model):
|
||||
__tablename__ = 'calendars'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
label = Column(String, unique=True)
|
||||
monday = Column(Integer, default=0)
|
||||
tuesday = Column(Integer, default=0)
|
||||
wednesday = Column(Integer, default=0)
|
||||
thursday = Column(Integer, default=0)
|
||||
friday = Column(Integer, default=0)
|
||||
saturday = Column(Integer, default=0)
|
||||
sunday = Column(Integer, default=0)
|
||||
|
||||
|
||||
#shape_id,shape_pt_lat,shape_pt_lon,shape_pt_sequence,shape_dist_traveled
|
||||
#1136979693,-36.843,-73.00984,1
|
||||
#1136979693,-36.843,-73.00984,2
|
||||
class Formas(Model):
|
||||
__tablename__ = 'shapes'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
shape_1pt_lat = Column(Double, nullable=False)
|
||||
shape_1pt_lon = Column(Double, nullable=False)
|
||||
|
||||
shape_num_pt = Column(Integer, nullable=False)
|
||||
shape_length = Column(Double)
|
||||
|
||||
class Segmentos(Model):
|
||||
__tablename__ = 'shapes_point'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
pt_start_lat = Column(Double, nullable=False)
|
||||
pt_start_lon = Column(Double, nullable=False)
|
||||
pt_end_lat = Column(Double, nullable=False)
|
||||
pt_end_lon = Column(Double, nullable=False)
|
||||
|
||||
shape_pt_sequence = Column(Integer, nullable=False)
|
||||
shape_slope = Column(Double)
|
||||
shape_dist_traveled = Column(Double)
|
||||
shape_id = Column(Integer, ForeignKey('shapes.id'), nullable=False)
|
||||
|
||||
__table_args__ = (UniqueConstraint('shape_id', 'shape_pt_sequence', name='_shapeid_sequence_uc'), )
|
||||
|
||||
|
||||
class Comunas(Model):
|
||||
__tablename__ = 'comuna'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
name = Column(String, unique=True)
|
||||
|
||||
|
||||
#stop_id,stop_code,stop_name,stop_desc,stop_lat,stop_lon,zone_id,stop_url,location_type,parent_station,wheelchair_boarding
|
||||
#40921,,Pque Zoologico Concepcion Poniente,,-36.8400453,-73.00696914,,,,,
|
||||
#40808,,Cno. Nonguen esq Las Vertientes,,-36.83675878,-73.00343935,,,,,
|
||||
class Paradas(Model):
|
||||
__tablename__ = 'stops'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
stop_code = Column(String, unique=True, nullable=True)
|
||||
stop_name = Column(String)
|
||||
stop_desc = Column(String)
|
||||
stop_lat = Column(Double, nullable=False)
|
||||
stop_lon = Column(Double, nullable=False)
|
||||
|
||||
comuna_id = Column(Integer, ForeignKey('comuna.id'), nullable=False)
|
||||
stop_url = Column(String)
|
||||
parent_station = Column(BigInteger)
|
||||
wheelchair_boarding = Column(Integer, default=0)
|
||||
|
||||
|
||||
#route_id,agency_id,route_short_name,route_long_name,route_desc,route_type,route_url,route_color,route_text_color
|
||||
#625,DTPR,70KH,Nonguén - Parque Tumbes,,3,,0d7215,ffffff
|
||||
#600,DTPR,41CR,Parque Empresarial Biobío - Terminal Collao,,3,,ad0101,ffffff
|
||||
class Lineas(Model):
|
||||
__tablename__ = 'routes'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
agency_id = Column(Integer, ForeignKey('agency.id'), nullable=False)
|
||||
route_short_name = Column(String)
|
||||
route_long_name = Column(String)
|
||||
route_desc = Column(String)
|
||||
route_url = Column(String)
|
||||
route_color = Column(Integer, default=0)
|
||||
route_text_color = Column(Integer, default=0)
|
||||
|
||||
|
||||
#route_id,service_id,trip_id,trip_headsign, direction_id,block_id,shape_id,wheelchair_accessible,bikes_allowed
|
||||
#route_id,service_id,trip_id,trip_headsign,trip_short_name,direction_id,block_id,shape_id,wheelchair_accessible,bikes_allowed
|
||||
#625,S,c8b17d5f-4-76aabf89-b,Nonguén,,1,,1136979694,,
|
||||
#625,S,4d018d35-7-76aabf89-b,Parque Tumbes,,0,,1136979693,,
|
||||
#ac48288b-1-56195eaf-8,b1910f5b,1,0,1168259538,,0,0,1-1,1
|
||||
#d9e94be9-f-56195eaf-8,b1910f5b,1,0,1168259538,,0,0,1-1,2
|
||||
class Servicio(Model):
|
||||
__tablename__ = 'trips'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
|
||||
route_id = Column(Integer, ForeignKey('routes.id'), nullable=False) # A que linea corresponde
|
||||
calendar_id = Column(Integer, ForeignKey('calendars.id'), nullable=False)
|
||||
gtfs_id = Column(Integer, ForeignKey('archivos.id'), nullable=False) # Identifiador unico del servicio
|
||||
shape_id = Column(Integer, ForeignKey('shapes.id'), nullable=False) # shapes.id
|
||||
direction_id = Column(Integer, nullable=False) # 0=ida, 1=vuelta
|
||||
|
||||
trip_headsign = Column(String)
|
||||
trip_short_name = Column(String)
|
||||
wheelchair_accessible = Column(Integer, default=0)
|
||||
bikes_allowed = Column(Integer, default=0)
|
||||
|
||||
#trip_id,arrival_time,departure_time,stop_id,stop_sequence, timepoint,shape_dist_traveled
|
||||
#trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type,timepoint
|
||||
#c8b17d5f-4-76aabf89-b,05:01:00,05:01:00,40439,1,,,,1
|
||||
#c8b17d5f-4-76aabf89-b,05:02:00,05:02:00,40440,2,,,,0
|
||||
class Detenciones(Model):
|
||||
__tablename__ = 'stop_times'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
trip_id = Column(Integer, ForeignKey('trips.id'), nullable=False)
|
||||
stop_sequence = Column(Integer, nullable=False)
|
||||
stop_id = Column(BigInteger, ForeignKey('stops.id'), nullable=False)
|
||||
|
||||
departure_time = Column(String(9), index=True)
|
||||
arrival_time = Column(String(9))
|
||||
dist_traveled = Column(Integer, default=0)
|
||||
|
||||
stop_headsign = Column(String)
|
||||
pickup_type = Column(Integer, default=0)
|
||||
drop_off_type = Column(Integer, default=0)
|
||||
timepoint = Column(Integer, default=1)
|
||||
|
||||
__table_args__ = (UniqueConstraint('trip_id', 'stop_sequence', name='_trip_sequence_uc'), )
|
||||
|
||||
# 4cc08782-c-76aabf89-b
|
|
@ -0,0 +1,49 @@
|
|||
import time
|
||||
from daemon import log
|
||||
from daemon.models.gtfs_work import session as dbhot
|
||||
from daemon.models.gtfs_static import session as dbstatic
|
||||
|
||||
|
||||
if not os.environ.get('HEARTBEAT'):
|
||||
hb = 60 * 15 # 15 minutos
|
||||
else:
|
||||
hb = int(os.environ.get('HEARTBEAT'))
|
||||
|
||||
|
||||
def main_loop(shouldIrun):
|
||||
|
||||
log.info("Iniciando el loop principal")
|
||||
doki=int(time.time()) + hb
|
||||
while shouldIrun:
|
||||
|
||||
if check_for_work():
|
||||
ingest_waiting_gtfs()
|
||||
|
||||
time.sleep(1)
|
||||
i = int(time.time())
|
||||
if i >= doki:
|
||||
doki = i + hb
|
||||
log.info('Heartbeat')
|
||||
|
||||
|
||||
def check_for_work():
|
||||
result = dbstatic.query(sArchivosGTFS).filter(sArchivosGTFS.loaded==0).first()
|
||||
|
||||
if result is not None:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def ingest_waiting_gtfs():
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def load_dataset(nombrearchivo):
|
||||
import zipfile
|
||||
import tempfile
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
with zipfile.ZipFile(nombrearchivo, 'r') as zip_ref:
|
||||
zip_ref.extractall(tmpdirname)
|
|
@ -0,0 +1,22 @@
|
|||
from daemon.service import main_loop
|
||||
from daemon.log import log
|
||||
import os
|
||||
import time
|
||||
import signal
|
||||
|
||||
shouldIrun = True
|
||||
|
||||
def main():
|
||||
|
||||
|
||||
def programCleanup(_signo, _stack_frame):
|
||||
log.info('Recibida la señal de salida')
|
||||
shouldIrun = False
|
||||
|
||||
signal.signal(signal.SIGTERM, programCleanup)
|
||||
|
||||
run(shouldIrun)
|
||||
log.info('Terminando Correctamente')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,155 @@
|
|||
version: '3.2'
|
||||
|
||||
services:
|
||||
## Bases de datos
|
||||
static_gtfs:
|
||||
image: docker.ilab.cl/tpmc_static_gtfs:devel
|
||||
restart: "no"
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- POSTGRES_PASSWORD=docker
|
||||
ports:
|
||||
- target: 5432
|
||||
published: 5401
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
work_gtfs:
|
||||
image: docker.ilab.cl/tpmc_work_gtfs:devel
|
||||
restart: "no"
|
||||
volumes:
|
||||
- pgdata_hot_gtfs:/var/lib/postgresql/data
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- POSTGRES_PASSWORD=docker
|
||||
ports:
|
||||
- target: 5432
|
||||
published: 5402
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
system_db:
|
||||
image: docker.ilab.cl/tpmc_system_db:devel
|
||||
restart: "no"
|
||||
volumes:
|
||||
- pgdata_web:/var/lib/postgresql/data
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- POSTGRES_PASSWORD=docker
|
||||
ports:
|
||||
- target: 5432
|
||||
published: 5403
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
## Contenedor de sitios estáticos
|
||||
front-static:
|
||||
image: docker.ilab.cl/tpmc_front_static:devel
|
||||
restart: "no"
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- SECRET_KEY=4d6f45a5fc12445dbac2f59c3b6c7cb2
|
||||
ports:
|
||||
- target: 8000
|
||||
published: 5001
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
## Contenedor del proceso de inicio de session
|
||||
login-logic:
|
||||
image: docker.ilab.cl/tpmc_login_logic:devel
|
||||
restart: "no"
|
||||
depends_on:
|
||||
- system_db
|
||||
- work_gtfs
|
||||
- static_gtfs
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- SECRET_KEY=4d6f45a5fc12445dbac2f59c3b6c7cb2
|
||||
- COOKIE_DOMAIN=tpmc.ilab.cl
|
||||
- SQLALCHEMY_WEBDB_URI=postgresql://docker:docker@system_db/docker
|
||||
- SQLALCHEMY_HOTDB_URI=postgresql://docker:docker@work_gtfs/docker
|
||||
- SQLALCHEMY_GTFSDB_URI=postgresql://docker:docker@static_gtfs/docker
|
||||
ports:
|
||||
- target: 8000
|
||||
published: 5002
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
## Contenedor de contenido dinamico
|
||||
public-dynamic:
|
||||
image: docker.ilab.cl/tpmc_public_dynamic:devel
|
||||
restart: "no"
|
||||
depends_on:
|
||||
- system_db
|
||||
- work_gtfs
|
||||
- static_gtfs
|
||||
volumes:
|
||||
- static_gtfs_volume:/srv/webinterface/static/gtfs_static
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- SECRET_KEY=4d6f45a5fc12445dbac2f59c3b6c7cb2
|
||||
- SQLALCHEMY_WEBDB_URI=postgresql://docker:docker@system_db/docker
|
||||
- SQLALCHEMY_HOTDB_URI=postgresql://docker:docker@work_gtfs/docker
|
||||
- SQLALCHEMY_GTFSDB_URI=postgresql://docker:docker@static_gtfs/docker
|
||||
- COOKIE_DOMAIN=tpmc.ilab.cl
|
||||
ports:
|
||||
- target: 8000
|
||||
published: 5003
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
## Contenedor de contenido dinamico
|
||||
private-app:
|
||||
image: docker.ilab.cl/tpmc_private_app:devel
|
||||
restart: "no"
|
||||
depends_on:
|
||||
- system_db
|
||||
- work_gtfs
|
||||
- static_gtfs
|
||||
volumes:
|
||||
- static_gtfs_volume:/srv/webinterface/static/gtfs_static
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- SECRET_KEY=4d6f45a5fc12445dbac2f59c3b6c7cb2
|
||||
- SQLALCHEMY_WEBDB_URI=postgresql://docker:docker@system_db/docker
|
||||
- SQLALCHEMY_HOTDB_URI=postgresql://docker:docker@work_gtfs/docker
|
||||
- SQLALCHEMY_GTFSDB_URI=postgresql://docker:docker@static_gtfs/docker
|
||||
- COOKIE_DOMAIN=tpmc.ilab.cl
|
||||
ports:
|
||||
- target: 8000
|
||||
published: 5004
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
## Carga GTFS en la DB
|
||||
load-gtfs:
|
||||
image: docker.ilab.cl/tpmc_load_gtfs:devel
|
||||
restart: "no"
|
||||
depends_on:
|
||||
- work_gtfs
|
||||
- static_gtfs
|
||||
volumes:
|
||||
- static_gtfs_volume:/srv/webinterface/static/gtfs_static
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- SQLALCHEMY_HOTDB_URI=postgresql://docker:docker@work_gtfs/docker
|
||||
- SQLALCHEMY_GTFSDB_URI=postgresql://docker:docker@static_gtfs/docker
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
volumes:
|
||||
static_gtfs_volume:
|
||||
external: true
|
||||
pgdata_web:
|
||||
external: true
|
||||
pgdata_hot_gtfs:
|
||||
external: true
|
|
@ -0,0 +1,150 @@
|
|||
version: '3.2'
|
||||
|
||||
services:
|
||||
## Bases de datos
|
||||
static_gtfs:
|
||||
build:
|
||||
dockerfile: Docker/Dockerfile.db_base
|
||||
context: .
|
||||
image: docker.ilab.cl/tpmc_static_gtfs:devel
|
||||
restart: "no"
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- POSTGRES_PASSWORD=docker
|
||||
ports:
|
||||
- 5401:5432
|
||||
|
||||
work_gtfs:
|
||||
build:
|
||||
dockerfile: Docker/Dockerfile.db_base
|
||||
context: .
|
||||
image: docker.ilab.cl/tpmc_work_gtfs:devel
|
||||
restart: "no"
|
||||
volumes:
|
||||
- pgdata_hot_gtfs:/var/lib/postgresql/data
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- POSTGRES_PASSWORD=docker
|
||||
ports:
|
||||
- 5402:5432
|
||||
|
||||
system_db:
|
||||
build:
|
||||
dockerfile: Docker/Dockerfile.db_web
|
||||
context: .
|
||||
image: docker.ilab.cl/tpmc_system_db:devel
|
||||
restart: "no"
|
||||
volumes:
|
||||
- pgdata_web:/var/lib/postgresql/data
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- POSTGRES_PASSWORD=docker
|
||||
ports:
|
||||
- 5403:5432
|
||||
|
||||
## Contenedor de sitios estáticos
|
||||
front-static:
|
||||
build:
|
||||
dockerfile: Docker/Dockerfile.front_static
|
||||
context: .
|
||||
image: docker.ilab.cl/tpmc_front_static:devel
|
||||
restart: "no"
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- SECRET_KEY=4d6f45a5fc12445dbac2f59c3b6c7cb2
|
||||
ports:
|
||||
- 5001:8000
|
||||
|
||||
## Contenedor del proceso de inicio de session
|
||||
login-logic:
|
||||
build:
|
||||
dockerfile: Docker/Dockerfile.login_system
|
||||
context: .
|
||||
image: docker.ilab.cl/tpmc_login_logic:devel
|
||||
restart: "no"
|
||||
depends_on:
|
||||
- system_db
|
||||
- work_gtfs
|
||||
- static_gtfs
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- SECRET_KEY=4d6f45a5fc12445dbac2f59c3b6c7cb2
|
||||
- COOKIE_DOMAIN=tpmc.ilab.cl
|
||||
- SQLALCHEMY_WEBDB_URI=postgresql://docker:docker@system_db/docker
|
||||
- SQLALCHEMY_HOTDB_URI=postgresql://docker:docker@work_gtfs/docker
|
||||
- SQLALCHEMY_GTFSDB_URI=postgresql://docker:docker@static_gtfs/docker
|
||||
ports:
|
||||
- 5002:8000
|
||||
|
||||
## Contenedor de contenido dinamico
|
||||
public-dynamic:
|
||||
build:
|
||||
dockerfile: Docker/Dockerfile.public_dynamic
|
||||
context: .
|
||||
image: docker.ilab.cl/tpmc_public_dynamic:devel
|
||||
restart: "no"
|
||||
depends_on:
|
||||
- system_db
|
||||
- work_gtfs
|
||||
- static_gtfs
|
||||
volumes:
|
||||
- static_gtfs_volume:/srv/webinterface/static/gtfs_static
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- SECRET_KEY=4d6f45a5fc12445dbac2f59c3b6c7cb2
|
||||
- SQLALCHEMY_WEBDB_URI=postgresql://docker:docker@system_db/docker
|
||||
- SQLALCHEMY_HOTDB_URI=postgresql://docker:docker@work_gtfs/docker
|
||||
- SQLALCHEMY_GTFSDB_URI=postgresql://docker:docker@static_gtfs/docker
|
||||
- COOKIE_DOMAIN=tpmc.ilab.cl
|
||||
ports:
|
||||
- 5003:8000
|
||||
|
||||
## Contenedor de contenido dinamico
|
||||
private-app:
|
||||
build:
|
||||
dockerfile: Docker/Dockerfile.private_dynamic
|
||||
context: .
|
||||
image: docker.ilab.cl/tpmc_private_app:devel
|
||||
restart: "no"
|
||||
depends_on:
|
||||
- system_db
|
||||
- work_gtfs
|
||||
- static_gtfs
|
||||
volumes:
|
||||
- static_gtfs_volume:/srv/webservice/static/gtfs_static
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- SECRET_KEY=4d6f45a5fc12445dbac2f59c3b6c7cb2
|
||||
- SQLALCHEMY_WEBDB_URI=postgresql://docker:docker@system_db/docker
|
||||
- SQLALCHEMY_HOTDB_URI=postgresql://docker:docker@work_gtfs/docker
|
||||
- SQLALCHEMY_GTFSDB_URI=postgresql://docker:docker@static_gtfs/docker
|
||||
- COOKIE_DOMAIN=tpmc.ilab.cl
|
||||
ports:
|
||||
- 5004:8000
|
||||
|
||||
|
||||
## Carga GTFS en la DB
|
||||
load-gtfs:
|
||||
build:
|
||||
dockerfile: Docker/Dockerfile.load_gtfs
|
||||
context: .
|
||||
image: docker.ilab.cl/tpmc_load_gtfs:devel
|
||||
restart: "no"
|
||||
depends_on:
|
||||
- work_gtfs
|
||||
- static_gtfs
|
||||
volumes:
|
||||
- static_gtfs_volume:/srv/webinterface/static/gtfs_static
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- SQLALCHEMY_HOTDB_URI=postgresql://docker:docker@work_gtfs/docker
|
||||
- SQLALCHEMY_GTFSDB_URI=postgresql://docker:docker@static_gtfs/docker
|
||||
|
||||
|
||||
volumes:
|
||||
static_gtfs_volume:
|
||||
external: true
|
||||
pgdata_web:
|
||||
external: true
|
||||
pgdata_hot_gtfs:
|
||||
external: true
|
|
@ -0,0 +1,39 @@
|
|||
# coding: utf-8
|
||||
from flask import Flask
|
||||
from flask.logging import default_handler
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
class Config:
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY')
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
SQLALCHEMY_ECHO = False
|
||||
DEBUG = os.environ.get('DEBUG')
|
||||
SESSION_COOKIE_DOMAIN = os.environ.get('COOKIE_DOMAIN')
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
|
||||
MAIL_DEBUG = int(os.environ.get('DEBUG') == True)
|
||||
|
||||
#if sys.version_info.major < 3:
|
||||
# reload(sys)
|
||||
#sys.setdefaultencoding('utf8')
|
||||
|
||||
|
||||
|
||||
def create_app(config_class=Config):
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(config_class)
|
||||
|
||||
if app.debug:
|
||||
app.logger.setLevel(logging.DEBUG)
|
||||
else:
|
||||
app.logger.setLevel(logging.INFO)
|
||||
|
||||
|
||||
|
||||
from webinterface.content.main import main
|
||||
app.register_blueprint(main)
|
||||
|
||||
return app
|
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from flask import Flask, Blueprint, abort, current_app, request, render_template, send_from_directory
|
||||
from datetime import datetime, date
|
||||
|
||||
|
||||
main = Blueprint('main', __name__)
|
||||
|
||||
@main.route('/repositorio')
|
||||
def repositorio():
|
||||
return render_template('repositorio.html')
|
||||
|
||||
@main.route('/objetivos')
|
||||
def about():
|
||||
return render_template('objetivos.html')
|
||||
|
||||
@main.route('/')
|
||||
def home():
|
||||
return render_template('home.html')
|
||||
|
||||
@main.route('/static/<path:path>')
|
||||
def static(path):
|
||||
return send_from_directory('static', path)
|
||||
|
||||
@main.route('/fonts/<path:path>')
|
||||
def static_fonts(path):
|
||||
return send_from_directory('static/fonts', path)
|
||||
|
||||
@main.route('/favicon.ico')
|
||||
def favicon():
|
||||
return send_from_directory(os.path.join(current_app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon')
|
|
@ -0,0 +1,6 @@
|
|||
Flask
|
||||
gunicorn
|
||||
Werkzeug
|
||||
ua-parser
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# coding: utf-8
|
||||
from flask import Flask
|
||||
from flask.logging import default_handler
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_login import LoginManager
|
||||
from flask_mail import Mail
|
||||
from flask_bcrypt import Bcrypt
|
||||
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
class Config:
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY')
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
SQLALCHEMY_ECHO = False
|
||||
DEBUG = os.environ.get('DEBUG')
|
||||
SESSION_COOKIE_DOMAIN = os.environ.get('COOKIE_DOMAIN')
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_WEBDB_URI')
|
||||
|
||||
SQLALCHEMY_BINDS = {
|
||||
'system': os.environ.get('SQLALCHEMY_WEBDB_URI'),
|
||||
'gtfs_work': os.environ.get('SQLALCHEMY_HOTDB_URI'),
|
||||
'gtfs_static': os.environ.get('SQLALCHEMY_GTFSDB_URI'),
|
||||
}
|
||||
MAIL_DEBUG = int(os.environ.get('DEBUG') == True)
|
||||
|
||||
#if sys.version_info.major < 3:
|
||||
# reload(sys)
|
||||
#sys.setdefaultencoding('utf8')
|
||||
|
||||
bcrypt = Bcrypt()
|
||||
mail = Mail()
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.login_view = 'main.login'
|
||||
login_manager.login_message = u'Para continuar, ingrese su nombre de usuario y contraseña.'
|
||||
login_manager.login_message_category = 'info'
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
def create_app(config_class=Config):
|
||||
app = Flask(__name__)
|
||||
|
||||
app.config.from_object(config_class)
|
||||
|
||||
login_manager.init_app(app)
|
||||
|
||||
if app.debug:
|
||||
app.logger.setLevel(logging.DEBUG)
|
||||
else:
|
||||
app.logger.setLevel(logging.INFO)
|
||||
|
||||
db.init_app(app)
|
||||
|
||||
from webinterface.content.main import main
|
||||
app.register_blueprint(main)
|
||||
|
||||
return app
|
|
@ -0,0 +1,55 @@
|
|||
# coding: utf-8
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, PasswordField, SubmitField, BooleanField
|
||||
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
|
||||
from flask_login import current_user
|
||||
from webinterface.models.system import Persona
|
||||
import pwnedpasswords
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
login = StringField('Rut', validators=[DataRequired()])
|
||||
clave = PasswordField('Clave', validators=[DataRequired()])
|
||||
remember = BooleanField('Recordarme')
|
||||
submit = SubmitField('Ingresar')
|
||||
|
||||
|
||||
class RequestResetForm(FlaskForm):
|
||||
correo = StringField('Correo',
|
||||
validators=[DataRequired(), Email()])
|
||||
submit = SubmitField(u'Solicitud de recuperación de clave')
|
||||
|
||||
|
||||
class ResetPasswordForm(FlaskForm):
|
||||
password = PasswordField('Ingresa una Clave de Acceso', validators=[DataRequired()])
|
||||
confirm_password = PasswordField(u'Confirma tu Clave',
|
||||
validators=[DataRequired(), EqualTo('password')])
|
||||
submit = SubmitField(u'Actualizar Contraseña')
|
||||
|
||||
def validate_password(self, clave):
|
||||
if pwnedpasswords.check(clave.data):
|
||||
raise ValidationError(u'La Clave ingresada es insegura. Verifíquelo en <a href="https://haveibeenpwned.com/Passwords">\';--have i been pwned?</a>')
|
||||
|
||||
|
||||
class RegistrationForm(FlaskForm):
|
||||
login = StringField('Usuario',
|
||||
validators=[DataRequired(), Length(min=2, max=20)])
|
||||
email = StringField('Correo',
|
||||
validators=[DataRequired(), Email()])
|
||||
password = PasswordField('Clave', validators=[DataRequired()])
|
||||
confirm_password = PasswordField('Confirma clave',
|
||||
validators=[DataRequired(), EqualTo('password')])
|
||||
submit = SubmitField('Registrar')
|
||||
|
||||
def validate_login(self, login):
|
||||
user = Persona.query.filter_by(login=login.data).first()
|
||||
if user:
|
||||
raise ValidationError(u'El nombre de usuario ya existe, elige otro por favor.')
|
||||
|
||||
def validate_email(self, email):
|
||||
correo = Correo.query.filter_by(correo=email.data).first()
|
||||
if correo:
|
||||
raise ValidationError(u'El correo electrónico ya exite. ¿Olvidaste tu clave?')
|
||||
|
||||
def validate_password(self, clave):
|
||||
if pwnedpasswords.check(clave.data):
|
||||
raise ValidationError(u'La clave ingresada es insegura. Verifíquelo en <a href="https://haveibeenpwned.com/Passwords">\';--have i been pwned?</a>')
|
|
@ -0,0 +1,159 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from flask import Flask, Blueprint, abort, session, g, current_app, request, render_template, send_from_directory, jsonify, flash, redirect
|
||||
from flask_login import login_user, current_user, logout_user, login_required
|
||||
|
||||
from user_agents import parse
|
||||
from webinterface import db, bcrypt
|
||||
from sqlalchemy import func
|
||||
from datetime import datetime, date
|
||||
from webinterface.models.system import Persona, Ipaddr, Dispositivo, Identidad, Sesion, Registro, Conexion, Ruta, Sitio, Correo
|
||||
from webinterface.content.forms import RegistrationForm, LoginForm, RequestResetForm, ResetPasswordForm
|
||||
from webinterface.content.utils import clean_str, es_local
|
||||
|
||||
main = Blueprint('main', __name__)
|
||||
|
||||
if os.environ.get('COOKIE_DOMAIN'):
|
||||
systemuri = "https://app.{}/".format(os.environ.get('COOKIE_DOMAIN'))
|
||||
else:
|
||||
systemuri = 'https://app.tpmc.ilab.cl/'
|
||||
|
||||
|
||||
@main.route("/system/login", methods=['GET', 'POST'])
|
||||
def login():
|
||||
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('main.dashboard'))
|
||||
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
form.login.data = clean_str(form.login.data.lower().strip())
|
||||
user = Persona.query.filter(Persona.rut==form.login.data).first()
|
||||
if user and bcrypt.check_password_hash(user.clave, form.clave.data):
|
||||
login_user(user, remember=form.remember.data)
|
||||
g.user = user
|
||||
g.sesion.identidad = user
|
||||
db.session.commit()
|
||||
|
||||
next_page = request.args.get('next')
|
||||
current_app.logger.info('Acceso exitoso: IP({}), USER({}), UA({})'.format(g.ip.ipaddr,form.login.data, g.dispositivo.parsed_ua))
|
||||
return redirect(next_page) if next_page else redirect(systemuri)
|
||||
else:
|
||||
flash(u'El nombre de usuario o la contraseña son incorrectos.', 'danger')
|
||||
current_app.logger.warning('Intento de ingreso fallido: IP({}), USER({}), UA({})'.format(g.ip.ipaddr,form.login.data, g.dispositivo.parsed_ua))
|
||||
return render_template('system/login.html', title='Ingreso de Usuarios del Sistema', form=form)
|
||||
|
||||
|
||||
@main.route("/system/logout")
|
||||
def logout():
|
||||
|
||||
logout_user()
|
||||
session.pop('sid', None)
|
||||
|
||||
return redirect('https://tpmc.ilab.cl/')
|
||||
|
||||
@main.route("/me")
|
||||
@login_required
|
||||
def me():
|
||||
image_file = url_for('static', filename='profile_pics/' + current_user.foto)
|
||||
return render_template('system/me.html', title=u'¿Quién soy?',
|
||||
image_file=image_file)
|
||||
|
||||
@main.before_app_request
|
||||
def registra_sesion():
|
||||
if request.headers.getlist("X-Forwarded-For"):
|
||||
remote_ip = request.headers.getlist("X-Forwarded-For")[0]
|
||||
else:
|
||||
remote_ip = request.environ['REMOTE_ADDR']
|
||||
uadata = parse(request.user_agent.string)
|
||||
|
||||
ip = Ipaddr.query.filter_by(ipaddr=remote_ip).one_or_none()
|
||||
if ip is None:
|
||||
ip = Ipaddr(ipaddr=remote_ip)
|
||||
db.session.add(ip)
|
||||
db.session.commit()
|
||||
|
||||
if 'sid' in session:
|
||||
sesion = Sesion.query.filter_by(id=session['sid']).one_or_none()
|
||||
if sesion is None:
|
||||
session.pop('sid', None)
|
||||
else:
|
||||
dispositivo = Dispositivo.query.filter_by(id=sesion.dispositivoid, parsed_ua=str(uadata)).one_or_none()
|
||||
if dispositivo is None:
|
||||
session.pop('sid', None)
|
||||
else:
|
||||
conexion = Conexion.query.filter_by(ipaddrid=ip.id, sesionid=sesion.id).one_or_none()
|
||||
Ident = sesion.identidad
|
||||
if conexion is None:
|
||||
conexion = Conexion(ipaddr=ip, sesion=sesion)
|
||||
db.session.add(conexion)
|
||||
db.session.commit()
|
||||
|
||||
if 'sid' not in session:
|
||||
|
||||
dispositivo = Dispositivo.query.filter_by(useragent=request.user_agent.string).one_or_none()
|
||||
if dispositivo is None:
|
||||
dev = uadata.is_pc and "PC" or uadata.device.family
|
||||
os = ("%s %s" % (uadata.os.family, uadata.os.version_string)).strip()
|
||||
browser = ("%s %s" % (uadata.browser.family, uadata.browser.version_string)).strip()
|
||||
|
||||
dispositivo = Dispositivo(useragent=request.user_agent.string, dev=dev , os=os , browser=browser, parsed_ua=str(uadata))
|
||||
db.session.add(dispositivo)
|
||||
db.session.commit()
|
||||
|
||||
if uadata.is_bot:
|
||||
Ident = Identidad.query.filter_by(login='Buscador').one()
|
||||
elif es_local(remote_ip):
|
||||
Ident = Identidad.query.filter_by(login='Intranet').one()
|
||||
else:
|
||||
Ident = Identidad.query.filter_by(login='Internet').one()
|
||||
|
||||
sesion = Sesion(identidad=Ident, dispositivo=dispositivo)
|
||||
db.session.add(sesion)
|
||||
db.session.commit()
|
||||
|
||||
conexion = Conexion(ipaddr=ip, sesion=sesion)
|
||||
db.session.add(conexion)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
# agregamos el registro asociado a la solicitud actual
|
||||
# a la sesion que esta actualmente cargada. Dejamos la sesion guardada en
|
||||
# la variable global g.
|
||||
|
||||
if '/reset_password' in str(request.path):
|
||||
rpth = '/reset_password'
|
||||
else:
|
||||
rpth = str(request.path)
|
||||
|
||||
solicitud = Ruta.query.filter_by(ruta=rpth).first()
|
||||
if solicitud is None:
|
||||
solicitud = Ruta(ruta=str(request.path))
|
||||
db.session.add(solicitud)
|
||||
db.session.commit()
|
||||
|
||||
host = Sitio.query.filter_by(sitio=request.host).first()
|
||||
if host is None:
|
||||
host = Sitio(sitio=request.host)
|
||||
db.session.add(host)
|
||||
db.session.commit()
|
||||
|
||||
tamano = int(request.headers.get('Content-Length') or 0)
|
||||
|
||||
now = datetime.now()
|
||||
|
||||
registro = Registro(sitio=host, ruta=solicitud, sesion=sesion, ipaddr=ip, tamano=tamano)
|
||||
db.session.add(registro)
|
||||
db.session.commit()
|
||||
|
||||
conexion.ultimo = now
|
||||
sesion.ultimo = now
|
||||
db.session.commit()
|
||||
|
||||
g.ip = ip
|
||||
g.user = Ident
|
||||
g.sesion = sesion
|
||||
g.conexion = conexion
|
||||
g.registro = registro
|
||||
g.dispositivo = dispositivo
|
||||
session['sid'] = g.sesion.id
|
|
@ -0,0 +1,35 @@
|
|||
# coding: utf-8
|
||||
import os
|
||||
from itertools import cycle
|
||||
|
||||
def es_local(ip='127.0.0.1'):
|
||||
from netaddr import IPAddress
|
||||
return IPAddress(ip).is_private()
|
||||
|
||||
def digito_verificador(rut):
|
||||
reversed_digits = map(int, reversed(str(rut)))
|
||||
factors = cycle(range(2, 8))
|
||||
s = sum(d * f for d, f in zip(reversed_digits, factors))
|
||||
return (-s) % 11 if (-s) % 11 != 10 else 'k'
|
||||
|
||||
def send_reset_email(correo):
|
||||
from flask import url_for, current_app
|
||||
from flask_mail import Message
|
||||
from ilab_app import mail
|
||||
|
||||
token = correo.cuenta.get_reset_token()
|
||||
msg = Message('Solicitud de recuperación de cuenta', sender='noreply@ilab.cl', recipients=[correo.correo])
|
||||
|
||||
msg.body = '''Para recuperar su cuenta, ingrese a la siguiente URL: {}
|
||||
|
||||
En caso que usted *no* haya realizado esta solicitud, simplemente ignore este correo y no se realizará ningún cambio.'''.format(url_for('main.reset_token', token=token, _external=True))
|
||||
current_app.logger.debug('Sending Dev({})'.format(msg.body))
|
||||
mail.send(msg)
|
||||
|
||||
def clean_telefono(mystr):
|
||||
import unidecode
|
||||
return unidecode.unidecode(mystr).replace(" ", "").replace("\t", "").replace("-", "").replace("'", "").replace(".", "").replace("\"", "").replace("%", "")
|
||||
|
||||
def clean_str(mystr):
|
||||
import unidecode
|
||||
return unidecode.unidecode(mystr).replace(" ", "").replace("\t", "").replace("'", "").replace(".", "").replace("\"", "").replace("%", "")
|
|
@ -0,0 +1,18 @@
|
|||
Flask
|
||||
Flask-Bcrypt
|
||||
Flask-Login
|
||||
Flask-Mail
|
||||
Flask-SQLAlchemy
|
||||
Flask-WTF
|
||||
flask_migrate
|
||||
psycopg2-binary
|
||||
SQLAlchemy
|
||||
gunicorn
|
||||
Werkzeug
|
||||
ua-parser
|
||||
user-agents
|
||||
MarkupSafe
|
||||
pwnedpasswords
|
||||
email_validator
|
||||
netaddr
|
||||
unidecode
|
|
@ -0,0 +1,44 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<div class="content-section">
|
||||
<form method="POST" action="">
|
||||
{{ form.hidden_tag() }}
|
||||
<fieldset class="form-group">
|
||||
<legend class="border-bottom mb-4">Ingreso</legend>
|
||||
<div class="form-group">
|
||||
{{ form.login.label(class="form-control-label") }}
|
||||
{% if form.login.errors %}
|
||||
{{ form.login(class="form-control form-control-lg is-invalid") }}
|
||||
<div class="invalid-feedback">
|
||||
{% for error in form.login.errors %}
|
||||
<span>{{ error.decode('utf-8') }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ form.login(class="form-control form-control-lg") }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.clave.label(class="form-control-label") }}
|
||||
{% if form.clave.errors %}
|
||||
{{ form.clave(class="form-control form-control-lg is-invalid") }}
|
||||
<div class="invalid-feedback">
|
||||
{% for error in form.clave.errors %}
|
||||
<span>{{ error.decode('utf-8') }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ form.clave(class="form-control form-control-lg") }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-check">
|
||||
{{ form.remember(class="form-check-input") }}
|
||||
{{ form.remember.label(class="form-check-label") }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.submit(class="btn btn-outline-info") }}
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
|
@ -0,0 +1,43 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<div class="card-header bg-primary"><h4 class="text-white"><i class="fas fa-user-circle mr-2"></i>{{ title }}</h4></div>
|
||||
<div class="content-section">
|
||||
<div class="media">
|
||||
<img class="rounded-circle account-img" src="{{ image_file }}">
|
||||
<div class="media-body">
|
||||
{% if not current_user.alias %}
|
||||
<h2 class="account-heading">{{ current_user.nombrecompleto }}</h2>
|
||||
{% else %}
|
||||
<h2 class="account-heading">{{ current_user.alias }}</h2>
|
||||
{% endif %}
|
||||
<p class="text-secondary">{{ current_user.correodefecto.correo }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<fieldset class="form-group">
|
||||
<legend class="border-bottom mb-4">Información de la Cuenta</legend>
|
||||
<div class="form-group">
|
||||
Nombre:
|
||||
{{ current_user.nombrecompleto }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
Login:
|
||||
{{ current_user.login }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
Correo:
|
||||
{{ current_user.correodefecto.correo }}
|
||||
</div>
|
||||
<legend class="border-bottom mb-4">Personalización</legend>
|
||||
<div class="form-group">
|
||||
Alias:
|
||||
{{ current_user.alias }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<a href="{{ url_for('main.dashboard') }}" class="btn btn-info btn-lg btn-block mt-2"><i class="fas fa-undo-alt mr-2"></i>Volver</a>
|
||||
|
||||
{% endblock content %}
|
|
@ -0,0 +1,13 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<article class="media content-section">
|
||||
|
||||
<div class="media-body">
|
||||
<legend class="border-bottom mb-4">Registro de Usuarios</legend>
|
||||
|
||||
<p class="article-content">El registro de cuentas se encuentra <strong>desactivado</strong>.</p>
|
||||
<p class="article-content">Si desea obtener una cuenta, debe solicitarla personalmente para evaluar su factibilidad.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{% endblock content %}
|
|
@ -0,0 +1,27 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<div class="content-section">
|
||||
<form method="POST" action="">
|
||||
{{ form.hidden_tag() }}
|
||||
<fieldset class="form-group">
|
||||
<legend class="border-bottom mb-4">{{ title }}</legend>
|
||||
<div class="form-group">
|
||||
{{ form.correo.label(class="form-control-label") }}
|
||||
{% if form.correo.errors %}
|
||||
{{ form.correo(class="form-control form-control-lg is-invalid") }}
|
||||
<div class="invalid-feedback">
|
||||
{% for error in form.correo.errors %}
|
||||
<span>{{ error }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ form.correo(class="form-control form-control-lg") }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group">
|
||||
{{ form.submit(class="btn btn-outline-info") }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
|
@ -0,0 +1,41 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<div class="content-section">
|
||||
<form method="POST" action="">
|
||||
{{ form.hidden_tag() }}
|
||||
<fieldset class="form-group">
|
||||
<legend class="border-bottom mb-4">{{ title }}</legend>
|
||||
<div class="form-group">Su nombre de usuario es: <b>{{user.login}}</b></div>
|
||||
<div class="form-group">
|
||||
{{ form.password.label(class="form-control-label") }}
|
||||
{% if form.password.errors %}
|
||||
{{ form.password(class="form-control form-control-lg is-invalid") }}
|
||||
<div class="invalid-feedback">
|
||||
{% for error in form.password.errors %}
|
||||
<span>{{ error|safe }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ form.password(class="form-control form-control-lg") }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.confirm_password.label(class="form-control-label") }}
|
||||
{% if form.confirm_password.errors %}
|
||||
{{ form.confirm_password(class="form-control form-control-lg is-invalid") }}
|
||||
<div class="invalid-feedback">
|
||||
{% for error in form.confirm_password.errors %}
|
||||
<span>{{ error|safe }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ form.confirm_password(class="form-control form-control-lg") }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group">
|
||||
{{ form.submit(class="btn btn-outline-info") }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
|
@ -0,0 +1,328 @@
|
|||
import os
|
||||
import io
|
||||
import csv
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from flask import current_app
|
||||
from webinterface import db
|
||||
from webinterface.models.gtfs_static import Agencia, Servicio, Paradero, Parada, Ruta, Linea
|
||||
|
||||
def parse_time(strinput):
|
||||
try:
|
||||
h, m, s = strinput.split(":")
|
||||
h = int(h)
|
||||
d = 0
|
||||
while h > 23:
|
||||
d += 1
|
||||
h -= 24
|
||||
|
||||
return datetime.strptime("{}:{}:{}".format(h,m,s), '%H:%M:%S').time(), d
|
||||
except:
|
||||
current_app.logger.debug('Traceback {}'.format(traceback.format_exc()))
|
||||
current_app.logger.warning("Hora incorrecta => {}".format(strinput))
|
||||
return datetime.strptime("01:01:01", '%H:%M:%S').time(), 0
|
||||
|
||||
|
||||
def parse_paraderoid(strinput):
|
||||
primerodenero2022 = 44562
|
||||
|
||||
if strinput.find("-") > 0:
|
||||
current_app.logger.error("Error en el dato de la parada: {}".format(strinput))
|
||||
try:
|
||||
whatdate= datetime.strptime('{}-{}'.format(strinput, 2022), '%d-%b-%Y').date()
|
||||
idparadero = primerodenero2022 + whatdate.timetuple().tm_yday
|
||||
current_app.logger.warning("Calculado el Identificador en base al año 2022 => {}".format(idparadero))
|
||||
return idparadero
|
||||
except:
|
||||
return 0
|
||||
else:
|
||||
return int(strinput)
|
||||
|
||||
|
||||
def completa_dataset():
|
||||
iparaderos = 0
|
||||
for paradero in Paradero.query.all():
|
||||
iparaderos += 1
|
||||
cparadero = 0
|
||||
lparadas = 0
|
||||
sparadas = 0
|
||||
dparadas = 0
|
||||
lineas = []
|
||||
lparada = []
|
||||
|
||||
for parada in Parada.query.filter(Parada.paraderoid==paradero.id).all():
|
||||
cparadero += 1
|
||||
if cparadero % 1000 == 0:
|
||||
current_app.logger.debug("Paradero {}, {} detenciones. {} lineas".format(iparaderos, cparadero, len(lineas)))
|
||||
|
||||
elemento = (parada.servicio.lineaid, parada.servicio.direccionid)
|
||||
|
||||
if parada.servicio.dotw == 'S':
|
||||
sparadas += 1
|
||||
elif parada.servicio.dotw == 'D':
|
||||
dparadas += 1
|
||||
else:
|
||||
lparadas += 1
|
||||
|
||||
if elemento in lineas:
|
||||
continue
|
||||
|
||||
lineas.append(elemento)
|
||||
if parada.servicio.direccionid:
|
||||
lparada.append("{} ({} ida)".format(parada.servicio.linea.codigo, parada.secuencia))
|
||||
else:
|
||||
lparada.append("{} ({} regreso)".format(parada.servicio.linea.codigo, parada.secuencia))
|
||||
|
||||
lparada.sort()
|
||||
paradero.recorridos = " - ".join(lparada)
|
||||
paradero.paradas = 5*lparadas+sparadas+dparadas
|
||||
paradero.resumen = "{} paradas a la semana, {} lineas".format(paradero.paradas, len(lineas))
|
||||
paradero.paradasl = lparadas
|
||||
paradero.paradass = sparadas
|
||||
paradero.paradasd = dparadas
|
||||
paradero.lineas = len(lineas)
|
||||
current_app.logger.info("Paradero {}, {} detenciones a la semana en {} lineas".format(iparaderos, paradero.paradas, len(lineas)))
|
||||
if iparaderos % 10 == 0:
|
||||
db.session.commit()
|
||||
|
||||
db.session.commit()
|
||||
|
||||
def agrega_paraderos():
|
||||
from bs4 import BeautifulSoup
|
||||
from zipfile import ZipFile
|
||||
|
||||
# print(os.getcwd())
|
||||
|
||||
current_app.logger.info("Archivo de paraderos, kmz")
|
||||
with ZipFile('webinterface/models/datos/PARADEROS_FORMALES.kmz', 'r') as kmz:
|
||||
kml = kmz.open(kmz.filelist[0].filename, 'r').read()
|
||||
|
||||
soup = BeautifulSoup(kml, 'xml')
|
||||
existen = 0
|
||||
nuevo = 0
|
||||
|
||||
|
||||
for paradero in soup.find_all("kml:Placemark"):
|
||||
if (existen+nuevo) % 1000 == 0:
|
||||
current_app.logger.debug("Leidos {} paraderos existentes y {} nuevos".format(existen, nuevo))
|
||||
|
||||
|
||||
lat = int(paradero.find("kml:SimpleData", {"name": "LATITUDE"}).text)/1000000
|
||||
lng = int(paradero.find("kml:SimpleData", {"name": "LONGITUDE"}).text)/1000000
|
||||
|
||||
codigo = paradero.find("kml:SimpleData", {"name": "CODIGO_INT"}).text
|
||||
comuna = paradero.find("kml:SimpleData", {"name": "COMUNA"}).text
|
||||
recorridos = paradero.find("kml:SimpleData", {"name": "RECORRIDOS"}).text
|
||||
tamano = paradero.find("kml:SimpleData", {"name": "SUPERFICIE"}).text
|
||||
capacidad = paradero.find("kml:SimpleData", {"name": "CAPACIDAD"}).text
|
||||
materialpiso = paradero.find("kml:SimpleData", {"name": "RADIER_MAT"}).text
|
||||
materialpared = paradero.find("kml:SimpleData", {"name": "PARED_POS2"}).text
|
||||
materialtecho = paradero.find("kml:SimpleData", {"name": "TECHUMBRE2"}).text
|
||||
|
||||
|
||||
match = None
|
||||
|
||||
for radio in [0.00000001, 0.00000002, 0.00000003, 0.00000004, 0.00000005, 0.000000075, 0.0000001, 0.0000002]:
|
||||
match = Paradero.query.filter(((Paradero.latitud-lat)*(Paradero.latitud-lat)+(Paradero.longitud-lng)*(Paradero.longitud-lng)) < radio).first()
|
||||
|
||||
if match is not None:
|
||||
break
|
||||
|
||||
if match is None:
|
||||
nuevo += 1
|
||||
nombre = "{} cerca {}".format(paradero.find("kml:SimpleData", {"name": "NOMBRE_DE_"}).text, paradero.find("kml:SimpleData", {"name": "CALLE_CERC"}).text)
|
||||
match = Paradero(id=codigo, codigo=codigo, nombre=nombre, latitud=lat, longitud=lng)
|
||||
db.session.add(match)
|
||||
db.session.commit()
|
||||
else:
|
||||
existen += 1
|
||||
|
||||
match.codigo = codigo
|
||||
match.comuna = comuna
|
||||
match.recorridos2 = recorridos
|
||||
match.tamano = tamano
|
||||
match.capacidad = capacidad
|
||||
match.techo = materialtecho
|
||||
match.pared = materialpared
|
||||
match.piso = materialpiso
|
||||
db.session.commit()
|
||||
|
||||
current_app.logger.info("Cargados {} paraderos existentes y {} nuevos".format(existen, nuevo))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def load_dataset():
|
||||
from bs4 import BeautifulSoup
|
||||
from zipfile import ZipFile
|
||||
|
||||
with ZipFile('GC.zip', 'r') as dataset:
|
||||
current_app.logger.debug("Abriendo archivo GC.zip")
|
||||
|
||||
#Carga las agencias:
|
||||
with io.TextIOWrapper(dataset.open('agency.txt'), encoding='utf8') as agencias_csv:
|
||||
current_app.logger.debug("Leyendo agency.txt")
|
||||
agencias = csv.reader(agencias_csv, delimiter=',')
|
||||
elementos = 0
|
||||
|
||||
for item in agencias:
|
||||
if elementos > 0:
|
||||
agencia = Agencia.query.filter(Agencia.id==item[0]).one_or_none()
|
||||
|
||||
if agencia is None:
|
||||
agencia = Agencia(id=item[0], nombre=item[1], url=item[2], fono=item[5])
|
||||
db.session.add(agencia)
|
||||
else:
|
||||
agencia.nombre = item[1]
|
||||
agencia.url = item[2]
|
||||
agencia.fono = item[3]
|
||||
|
||||
elementos += 1
|
||||
|
||||
db.session.commit()
|
||||
current_app.logger.info("Se cargaron {} agencias".format(elementos))
|
||||
|
||||
|
||||
#Carga las agencias
|
||||
with io.TextIOWrapper(dataset.open('stops.txt'), encoding='utf8') as paraderos_csv:
|
||||
current_app.logger.debug("Leyendo stops.txt")
|
||||
paraderos = csv.reader(paraderos_csv, delimiter=',')
|
||||
elementos = 0
|
||||
|
||||
for item in paraderos:
|
||||
if elementos > 0:
|
||||
|
||||
idparadero = item[0]
|
||||
|
||||
paradero = Paradero.query.filter(Paradero.id==idparadero).one_or_none()
|
||||
if len(item[1]) > 0:
|
||||
codigo = item[1]
|
||||
else:
|
||||
codigo = None
|
||||
|
||||
if paradero is None:
|
||||
|
||||
paradero = Paradero(id=idparadero, codigo=codigo, nombre=item[2], latitud=item[4], longitud=item[5])
|
||||
db.session.add(paradero)
|
||||
else:
|
||||
paradero.codigo = codigo
|
||||
paradero.nombre = item[2]
|
||||
paradero.latitud = item[4]
|
||||
paradero.longitud = item[5]
|
||||
|
||||
elementos += 1
|
||||
if elementos % 1000 == 0:
|
||||
current_app.logger.info("Se han cargado {} elementos".format(elementos))
|
||||
|
||||
db.session.commit()
|
||||
current_app.logger.info("Se cargaron {} paradas".format(elementos))
|
||||
|
||||
|
||||
with io.TextIOWrapper(dataset.open('routes.txt'), encoding='utf8') as lineas_csv:
|
||||
current_app.logger.debug("Leyendo routes.txt")
|
||||
lineas = csv.reader(lineas_csv, delimiter=',')
|
||||
elementos = 0
|
||||
|
||||
for item in lineas:
|
||||
if elementos > 0:
|
||||
variante = Linea.query.filter(Linea.id==item[0]).one_or_none()
|
||||
|
||||
if variante is None:
|
||||
variante = Linea(id=item[0], agenciaid=item[1], codigo=item[2], nombre=item[3], color=item[7])
|
||||
db.session.add(variante)
|
||||
else:
|
||||
variante.agenciaid = item[1]
|
||||
variante.codigo = item[2]
|
||||
variante.nombre = item[3]
|
||||
variante.color = item[7]
|
||||
|
||||
elementos += 1
|
||||
if elementos % 1000 == 0:
|
||||
current_app.logger.info("Se han cargado {} elementos".format(elementos))
|
||||
|
||||
db.session.commit()
|
||||
current_app.logger.info("Se cargaron {} Linea".format(elementos))
|
||||
|
||||
|
||||
with io.TextIOWrapper(dataset.open('trips.txt'), encoding='utf8') as rutas_csv:
|
||||
current_app.logger.debug("Leyendo Servicios.txt")
|
||||
rutas = csv.reader(rutas_csv, delimiter=',')
|
||||
elementos = 0
|
||||
|
||||
for item in rutas:
|
||||
if elementos > 0:
|
||||
|
||||
itinerario = Servicio.query.filter(Servicio.id==item[2], Servicio.lineaid==item[0]).one_or_none()
|
||||
|
||||
if itinerario is None:
|
||||
itinerario = Servicio(id=item[2], lineaid=item[0], direccionid=item[5], destino=item[3], dotw=item[1], rutaid=item[7])
|
||||
db.session.add(itinerario)
|
||||
else:
|
||||
itinerario.destino = item[3]
|
||||
itinerario.direccionid = item[5]
|
||||
itinerario.rutaid = item[7]
|
||||
itinerario.dotw = item[1]
|
||||
|
||||
elementos += 1
|
||||
if elementos % 1000 == 0:
|
||||
db.session.commit()
|
||||
current_app.logger.info("Se han cargado {} elementos".format(elementos))
|
||||
|
||||
db.session.commit()
|
||||
current_app.logger.info("Se cargaron {} servicios".format(elementos))
|
||||
|
||||
with io.TextIOWrapper(dataset.open('shapes.txt'), encoding='utf8') as shapes_csv:
|
||||
current_app.logger.debug("Leyendo rutas.txt")
|
||||
shapes = csv.reader(shapes_csv, delimiter=',')
|
||||
elementos = 0
|
||||
|
||||
for item in shapes:
|
||||
if elementos > 0:
|
||||
shape = Ruta.query.filter(Ruta.rutaid==item[0], Ruta.secuencia==item[3]).one_or_none()
|
||||
|
||||
if shape is None:
|
||||
shape = Ruta(rutaid=item[0], secuencia=item[3], latitud=item[1], longitud=item[2])
|
||||
db.session.add(shape)
|
||||
else:
|
||||
shape.latitud = item[1]
|
||||
shape.longitud = item[2]
|
||||
|
||||
elementos += 1
|
||||
if elementos % 1000 == 0:
|
||||
db.session.commit()
|
||||
current_app.logger.info("Se han cargado {} elementos".format(elementos))
|
||||
|
||||
|
||||
db.session.commit()
|
||||
current_app.logger.info("Se cargaron {} puntos".format(elementos))
|
||||
|
||||
with io.TextIOWrapper(dataset.open('stop_times.txt'), encoding='utf8') as paradas_csv:
|
||||
current_app.logger.debug("Leyendo paradas.txt")
|
||||
paradas = csv.reader(paradas_csv, delimiter=',')
|
||||
elementos = 0
|
||||
|
||||
for item in paradas:
|
||||
if elementos > 0:
|
||||
idparadero = item[3]
|
||||
|
||||
parada = Parada.query.filter(Parada.servicioid==item[0], Parada.paraderoid==idparadero, Parada.secuencia==item[4]).one_or_none()
|
||||
llegada, _ = parse_time(item[1])
|
||||
salida, otrodia = parse_time(item[2])
|
||||
|
||||
if parada is None:
|
||||
parada = Parada(servicioid=item[0], paraderoid=idparadero, secuencia=item[4], tipo=item[8], llegada=llegada, salida=salida, otrodia=otrodia)
|
||||
db.session.add(parada)
|
||||
else:
|
||||
parada.llegada = llegada
|
||||
parada.salida = salida
|
||||
parada.otrodia = otrodia
|
||||
parada.tipo = item[8]
|
||||
|
||||
elementos += 1
|
||||
if elementos % 1000 == 0:
|
||||
db.session.commit()
|
||||
current_app.logger.info("Se han cargado {} elementos".format(elementos))
|
||||
|
||||
db.session.commit()
|
||||
current_app.logger.info("Se cargaron {} paradas".format(elementos))
|
|
@ -0,0 +1,164 @@
|
|||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from webinterface import db
|
||||
|
||||
class sArchivosGTFS(db.Model):
|
||||
__tablename__ = 'gtfs'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
descripcion = db.Column(db.String)
|
||||
file = db.Column(db.String)
|
||||
hash = db.Column(db.String)
|
||||
size = db.Column(db.Integer)
|
||||
loaded = db.Column(db.Integer, default=0)
|
||||
ts = db.Column(db.Datetime, default=func.now())
|
||||
|
||||
#agency_id,agency_name,agency_url,agency_timezone,agency_lang,agency_phone,agency_fare_url
|
||||
#DTPR,División de Transporte Público Regional,http://www.dtpr.gob.cl/,America/Santiago,es,+562 2421 3580,
|
||||
class sAgencia(db.Model):
|
||||
__tablename__ = 'agency'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
agency_id = db.Column(db.String)
|
||||
agency_name = db.Column(db.String)
|
||||
agency_url = db.Column(db.String)
|
||||
agency_timezone = db.Column(db.String)
|
||||
agency_lang = db.Column(db.String)
|
||||
agency_phone = db.Column(db.String)
|
||||
agency_fare_url = db.Column(db.String)
|
||||
|
||||
#service_id,start_date,end_date,monday,tuesday,wednesday,thursday,friday,saturday,sunday
|
||||
#S,20210101,20311231,0,0,0,0,0,1,0
|
||||
#D,20210101,20311231,0,0,0,0,0,0,1
|
||||
#L,20210101,20311231,1,1,1,1,1,0,0
|
||||
class sCalendario(db.Model):
|
||||
__tablename__ = 'calendar'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
service_id = db.Column(db.String)
|
||||
start_date = db.Column(db.String)
|
||||
end_date = db.Column(db.String)
|
||||
monday = db.Column(db.Integer)
|
||||
tuesday = db.Column(db.Integer)
|
||||
wednesday = db.Column(db.Integer)
|
||||
thursday = db.Column(db.Integer)
|
||||
friday = db.Column(db.Integer)
|
||||
saturday = db.Column(db.Integer)
|
||||
sunday = db.Column(db.Integer)
|
||||
|
||||
#feed_publisher_name,feed_publisher_url,feed_lang,feed_start_date,feed_end_date,feed_version
|
||||
#División de Transporte Público Regional,http://www.dtpr.gob.cl/,es,20210101,20311231,Gran Concepción20220616
|
||||
class sFeedInfo(db.Model):
|
||||
__tablename__ = 'feed_info'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
feed_publisher_name = db.Column(db.String)
|
||||
feed_publisher_url = db.Column(db.String)
|
||||
feed_lang = db.Column(db.String)
|
||||
feed_start_date = db.Column(db.String)
|
||||
feed_end_date = db.Column(db.String)
|
||||
feed_version = db.Column(db.String)
|
||||
|
||||
#route_id,agency_id,route_short_name,route_long_name,route_desc,route_type,route_url,route_color,route_text_color
|
||||
#625,DTPR,70KH,Nonguén - Parque Tumbes,,3,,0d7215,ffffff
|
||||
#600,DTPR,41CR,Parque Empresarial Biobío - Terminal Collao,,3,,ad0101,ffffff
|
||||
class sRuta(db.Model):
|
||||
__tablename__ = 'routes'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
route_id = db.Column(db.String)
|
||||
agency_id = db.Column(db.String)
|
||||
route_short_name = db.Column(db.String)
|
||||
route_long_name = db.Column(db.String)
|
||||
route_desc = db.Column(db.String)
|
||||
route_type = db.Column(db.Integer)
|
||||
route_url = db.Column(db.String)
|
||||
route_color = db.Column(db.String)
|
||||
route_text_color = db.Column(db.String)
|
||||
|
||||
#shape_id,shape_pt_lat,shape_pt_lon,shape_pt_sequence,shape_dist_traveled
|
||||
#1136979693,-36.843,-73.00984,1
|
||||
#1136979693,-36.843,-73.00984,2
|
||||
class sFormas(db.Model):
|
||||
__tablename__ = 'shapes'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
shape_id = db.Column(db.String)
|
||||
shape_pt_lat = db.Column(db.Double)
|
||||
shape_pt_lon = db.Column(db.Double)
|
||||
shape_pt_sequence = db.Column(db.Integer)
|
||||
shape_dist_traveled = db.Column(db.Double)
|
||||
|
||||
#stop_id,stop_code,stop_name,stop_desc,stop_lat,stop_lon,zone_id,stop_url,location_type,parent_station,wheelchair_boarding
|
||||
#40921,,Pque Zoologico Concepcion Poniente,,-36.8400453,-73.00696914,,,,,
|
||||
#40808,,Cno. Nonguen esq Las Vertientes,,-36.83675878,-73.00343935,,,,,
|
||||
class sParadas(db.Model):
|
||||
__tablename__ = 'stops'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
stop_id = db.Column(db.String)
|
||||
stop_code = db.Column(db.String)
|
||||
stop_name = db.Column(db.String)
|
||||
stop_desc = db.Column(db.String)
|
||||
stop_lat = db.Column(db.Double)
|
||||
stop_lon = db.Column(db.Double)
|
||||
zone_id = db.Column(db.String)
|
||||
stop_url = db.Column(db.String)
|
||||
location_type = db.Column(db.Integer)
|
||||
parent_station = db.Column(db.String)
|
||||
wheelchair_boarding = db.Column(db.Integer)
|
||||
#trip_id,arrival_time,departure_time,stop_id,stop_sequence, timepoint,shape_dist_traveled
|
||||
#trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type,timepoint
|
||||
#c8b17d5f-4-76aabf89-b,05:01:00,05:01:00,40439,1,,,,1
|
||||
#c8b17d5f-4-76aabf89-b,05:02:00,05:02:00,40440,2,,,,0
|
||||
class sDetenciones(db.Model):
|
||||
__tablename__ = 'stop_times'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
trip_id = db.Column(db.String)
|
||||
arrival_time = db.Column(db.String)
|
||||
departure_time = db.Column(db.String)
|
||||
stop_id = db.Column(db.String)
|
||||
stop_sequence = db.Column(db.Integer)
|
||||
stop_headsign = db.Column(db.String)
|
||||
pickup_type = db.Column(db.Integer)
|
||||
drop_off_type = db.Column(db.Integer)
|
||||
timepoint = db.Column(db.Integer)
|
||||
#,,,,,,
|
||||
|
||||
#route_id,service_id,trip_id,trip_headsign, direction_id,block_id,shape_id,wheelchair_accessible,bikes_allowed
|
||||
#route_id,service_id,trip_id,trip_headsign,trip_short_name,direction_id,block_id,shape_id,wheelchair_accessible,bikes_allowed
|
||||
#625,S,c8b17d5f-4-76aabf89-b,Nonguén,,1,,1136979694,,
|
||||
#625,S,4d018d35-7-76aabf89-b,Parque Tumbes,,0,,1136979693,,
|
||||
#ac48288b-1-56195eaf-8,b1910f5b,1,0,1168259538,,0,0,1-1,1
|
||||
#d9e94be9-f-56195eaf-8,b1910f5b,1,0,1168259538,,0,0,1-1,2
|
||||
class sServicio(db.Model):
|
||||
__tablename__ = 'trips'
|
||||
__bind_key__ = 'gtfs_static'
|
||||
|
||||
id = db.Column(db.String, primary_key=True, nullable=False)
|
||||
|
||||
route_id = db.Column(db.String) # Ruta.rutaid
|
||||
service_id = db.Column(db.String)
|
||||
trip_id = db.Column(db.String)
|
||||
trip_headsign = db.Column(db.String)
|
||||
trip_short_name = db.Column(db.String)
|
||||
direction_id = db.Column(db.Integer)
|
||||
block_id = db.Column(db.String)
|
||||
shape_id = db.Column(db.String)
|
||||
wheelchair_accessible = db.Column(db.Integer)
|
||||
bikes_allowed = db.Column(db.Integer)
|
||||
|
||||
# 4cc08782-c-76aabf89-b
|
|
@ -0,0 +1,168 @@
|
|||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from webinterface import db
|
||||
|
||||
class ArchivosGTFS(db.Model):
|
||||
__tablename__ = 'archivos'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False)
|
||||
|
||||
analisis = db.Column(db.Integer, default=0)
|
||||
terminado = db.Column(db.Integer, default=0)
|
||||
activo = db.Column(db.Integer, default=0)
|
||||
|
||||
class Agencia(db.Model):
|
||||
__tablename__ = 'agency'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
agency_id = db.Column(db.String, unique=True)
|
||||
agency_name = db.Column(db.String)
|
||||
agency_url = db.Column(db.String)
|
||||
agency_timezone = db.Column(db.String)
|
||||
agency_lang = db.Column(db.String)
|
||||
agency_phone = db.Column(db.String)
|
||||
agency_fare_url = db.Column(db.String)
|
||||
|
||||
class Calendario(db.Model):
|
||||
__tablename__ = 'calendars'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
label = db.Column(db.String, unique=True)
|
||||
monday = db.Column(db.Integer, default=0)
|
||||
tuesday = db.Column(db.Integer, default=0)
|
||||
wednesday = db.Column(db.Integer, default=0)
|
||||
thursday = db.Column(db.Integer, default=0)
|
||||
friday = db.Column(db.Integer, default=0)
|
||||
saturday = db.Column(db.Integer, default=0)
|
||||
sunday = db.Column(db.Integer, default=0)
|
||||
|
||||
|
||||
#shape_id,shape_pt_lat,shape_pt_lon,shape_pt_sequence,shape_dist_traveled
|
||||
#1136979693,-36.843,-73.00984,1
|
||||
#1136979693,-36.843,-73.00984,2
|
||||
class Formas(db.Model):
|
||||
__tablename__ = 'shapes'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
shape_1pt_lat = db.Column(db.Double, nullable=False)
|
||||
shape_1pt_lon = db.Column(db.Double, nullable=False)
|
||||
|
||||
shape_num_pt = db.Column(db.Integer, nullable=False)
|
||||
shape_length = db.Column(db.Double)
|
||||
|
||||
class Segmentos(db.Model):
|
||||
__tablename__ = 'shapes_point'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
pt_start_lat = db.Column(db.Double, nullable=False)
|
||||
pt_start_lon = db.Column(db.Double, nullable=False)
|
||||
pt_end_lat = db.Column(db.Double, nullable=False)
|
||||
pt_end_lon = db.Column(db.Double, nullable=False)
|
||||
|
||||
shape_pt_sequence = db.Column(db.Integer, nullable=False)
|
||||
shape_dist_traveled = db.Column(db.Double)
|
||||
shape_id = db.Column(db.Integer, db.ForeignKey('shapes.id'), nullable=False)
|
||||
|
||||
__table_args__ = (db.UniqueConstraint('shape_id', 'shape_pt_sequence', name='_shapeid_sequence_uc'), )
|
||||
|
||||
|
||||
class Comunas(db.Model):
|
||||
__tablename__ = 'comuna'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
name = db.Column(db.String, unique=True)
|
||||
|
||||
|
||||
#stop_id,stop_code,stop_name,stop_desc,stop_lat,stop_lon,zone_id,stop_url,location_type,parent_station,wheelchair_boarding
|
||||
#40921,,Pque Zoologico Concepcion Poniente,,-36.8400453,-73.00696914,,,,,
|
||||
#40808,,Cno. Nonguen esq Las Vertientes,,-36.83675878,-73.00343935,,,,,
|
||||
class Paradas(db.Model):
|
||||
__tablename__ = 'stops'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = db.Column(db.BigInteger, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
stop_code = db.Column(db.String, unique=True, nullable=True)
|
||||
stop_name = db.Column(db.String)
|
||||
stop_desc = db.Column(db.String)
|
||||
stop_lat = db.Column(db.Double, nullable=False)
|
||||
stop_lon = db.Column(db.Double, nullable=False)
|
||||
|
||||
comuna_id = db.Column(db.Integer, db.ForeignKey('comuna.id'), nullable=False)
|
||||
stop_url = db.Column(db.String)
|
||||
parent_station = db.Column(db.BigInteger)
|
||||
wheelchair_boarding = db.Column(db.Integer, default=0)
|
||||
|
||||
|
||||
#route_id,agency_id,route_short_name,route_long_name,route_desc,route_type,route_url,route_color,route_text_color
|
||||
#625,DTPR,70KH,Nonguén - Parque Tumbes,,3,,0d7215,ffffff
|
||||
#600,DTPR,41CR,Parque Empresarial Biobío - Terminal Collao,,3,,ad0101,ffffff
|
||||
class Lineas(db.Model):
|
||||
__tablename__ = 'routes'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
agency_id = db.Column(db.Integer, db.ForeignKey('agency.id'), nullable=False)
|
||||
route_short_name = db.Column(db.String)
|
||||
route_long_name = db.Column(db.String)
|
||||
route_desc = db.Column(db.String)
|
||||
route_url = db.Column(db.String)
|
||||
route_color = db.Column(db.Integer, default=0)
|
||||
route_text_color = db.Column(db.Integer, default=0)
|
||||
|
||||
|
||||
#route_id,service_id,trip_id,trip_headsign, direction_id,block_id,shape_id,wheelchair_accessible,bikes_allowed
|
||||
#route_id,service_id,trip_id,trip_headsign,trip_short_name,direction_id,block_id,shape_id,wheelchair_accessible,bikes_allowed
|
||||
#625,S,c8b17d5f-4-76aabf89-b,Nonguén,,1,,1136979694,,
|
||||
#625,S,4d018d35-7-76aabf89-b,Parque Tumbes,,0,,1136979693,,
|
||||
#ac48288b-1-56195eaf-8,b1910f5b,1,0,1168259538,,0,0,1-1,1
|
||||
#d9e94be9-f-56195eaf-8,b1910f5b,1,0,1168259538,,0,0,1-1,2
|
||||
class Servicio(db.Model):
|
||||
__tablename__ = 'trips'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False)
|
||||
|
||||
route_id = db.Column(db.Integer, db.ForeignKey('routes.id'), nullable=False) # A que linea corresponde
|
||||
calendar_id = db.Column(db.Integer, db.ForeignKey('calendars.id'), nullable=False)
|
||||
gtfs_id = db.Column(db.Integer, db.ForeignKey('archivos.id'), nullable=False) # Identifiador unico del servicio
|
||||
shape_id = db.Column(db.Integer, db.ForeignKey('shapes.id'), nullable=False) # shapes.id
|
||||
direction_id = db.Column(db.Integer, nullable=False) # 0=ida, 1=vuelta
|
||||
|
||||
trip_headsign = db.Column(db.String)
|
||||
trip_short_name = db.Column(db.String)
|
||||
wheelchair_accessible = db.Column(db.Integer, default=0)
|
||||
bikes_allowed = db.Column(db.Integer, default=0)
|
||||
|
||||
#trip_id,arrival_time,departure_time,stop_id,stop_sequence, timepoint,shape_dist_traveled
|
||||
#trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type,timepoint
|
||||
#c8b17d5f-4-76aabf89-b,05:01:00,05:01:00,40439,1,,,,1
|
||||
#c8b17d5f-4-76aabf89-b,05:02:00,05:02:00,40440,2,,,,0
|
||||
class Detenciones(db.Model):
|
||||
__tablename__ = 'stop_times'
|
||||
__bind_key__ = 'gtfs_work'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
|
||||
trip_id = db.Column(db.Integer, db.ForeignKey('trips.id'), nullable=False)
|
||||
stop_sequence = db.Column(db.Integer, nullable=False)
|
||||
stop_id = db.Column(db.BigInteger, db.ForeignKey('stops.id'), nullable=False)
|
||||
|
||||
departure_time = db.Column(db.String(9), index=True)
|
||||
arrival_time = db.Column(db.String(9))
|
||||
dist_traveled = db.Column(db.Integer, default=0)
|
||||
|
||||
stop_headsign = db.Column(db.String)
|
||||
pickup_type = db.Column(db.Integer, default=0)
|
||||
drop_off_type = db.Column(db.Integer, default=0)
|
||||
timepoint = db.Column(db.Integer, default=1)
|
||||
|
||||
__table_args__ = (db.UniqueConstraint('trip_id', 'stop_sequence', name='_trip_sequence_uc'), )
|
||||
|
||||
# 4cc08782-c-76aabf89-b
|
|
@ -0,0 +1,262 @@
|
|||
#from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
|
||||
#from datetime import datetime
|
||||
#from flask import current_app
|
||||
from flask_login import UserMixin
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.dialects import postgresql, sqlite
|
||||
from webinterface import db, login_manager
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return Persona.query.filter(Persona.rut==user_id).one_or_none()
|
||||
|
||||
class Identidad(db.Model):
|
||||
__tablename__ = 'identidades'
|
||||
__table_args__ = { 'schema': 'usuarios' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), primary_key=True, autoincrement=True)
|
||||
|
||||
login = db.Column(db.String(50), unique=True, nullable=False)
|
||||
alias = db.Column(db.String(50))
|
||||
|
||||
creado = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||
modificado = db.Column(db.DateTime(timezone=True), server_onupdate=func.now())
|
||||
|
||||
sesiones = db.relationship('Sesion', back_populates='identidad', lazy=True)
|
||||
ubicaciones = db.relationship('Ubicacion', back_populates='identidad', lazy=True)
|
||||
|
||||
tipo = db.Column(db.String(20))
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity':'Identidad',
|
||||
'polymorphic_on':tipo
|
||||
|
||||
}
|
||||
|
||||
class Cuenta(Identidad):
|
||||
__tablename__ = 'cuentas'
|
||||
__table_args__ = { 'schema': 'usuarios' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), db.ForeignKey('usuarios.identidades.id'), primary_key=True)
|
||||
|
||||
clave = db.Column(db.String(60), nullable=False)
|
||||
sysadmin = db.Column(db.Boolean, default=0)
|
||||
ultimoacceso = db.Column(db.DateTime(timezone=True))
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity':'Cuenta'
|
||||
}
|
||||
|
||||
correos = db.relationship('Correo', back_populates='cuenta', lazy=True)
|
||||
|
||||
def set_default(self, correo):
|
||||
for item in self.correos:
|
||||
if correo == item:
|
||||
correo.default = True
|
||||
else:
|
||||
correo.default = False
|
||||
|
||||
@property
|
||||
def correodefecto(self):
|
||||
last = None
|
||||
for item in self.correos:
|
||||
if item.default == True:
|
||||
return item
|
||||
elif last is None:
|
||||
last = item
|
||||
|
||||
return last
|
||||
|
||||
@correodefecto.setter
|
||||
def correodefecto(self, correo):
|
||||
if correo.cuenta != self:
|
||||
pass
|
||||
|
||||
for item in self.correos:
|
||||
if item == correo:
|
||||
item.default = True
|
||||
else:
|
||||
item.default = False
|
||||
|
||||
class Persona(Cuenta,UserMixin):
|
||||
__tablename__ = 'personas'
|
||||
__table_args__ = { 'schema': 'usuarios' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), db.ForeignKey('usuarios.cuentas.id'), primary_key=True)
|
||||
|
||||
nombres = db.Column(db.String(100), nullable=False)
|
||||
apellidop = db.Column(db.String(100), nullable=False)
|
||||
apellidom = db.Column(db.String(100))
|
||||
|
||||
rut = db.Column(db.String(20), unique=True, nullable=False)
|
||||
telefono = db.Column(db.String(20), nullable=True)
|
||||
foto = db.Column(db.String(50), nullable=False, default='default.jpg')
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity':'Persona'
|
||||
}
|
||||
def get_id(self):
|
||||
return self.rut
|
||||
|
||||
@property
|
||||
def nombrecompleto(self):
|
||||
if self.apellidom is not None:
|
||||
return u"{} {} {}.".format(self.nombres.partition(' ')[0], self.apellidop, self.apellidom[0])
|
||||
else:
|
||||
return u"{} {}.".format(self.nombres.partition(' ')[0], self.apellidop)
|
||||
@property
|
||||
def safe_nombrecompleto(self):
|
||||
return "{}".format(self.nombrecompleto.encode('utf-8'))
|
||||
@property
|
||||
def ascii_nombrecompleto(self):
|
||||
return unidecode.unidecode(self.nombrecompleto)
|
||||
|
||||
def is_in(self, comision=0):
|
||||
return True
|
||||
|
||||
|
||||
class Correo(db.Model):
|
||||
__tablename__ = 'correos'
|
||||
__table_args__ = { 'schema': 'usuarios' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
correo = db.Column(db.String(100), unique=True, nullable=False)
|
||||
|
||||
cuentaid = db.Column(db.BigInteger(), db.ForeignKey('usuarios.cuentas.id'), nullable=False)
|
||||
cuenta = db.relationship('Cuenta', back_populates='correos')
|
||||
default = db.Column(db.Boolean, default=0)
|
||||
|
||||
|
||||
class Sesion(db.Model):
|
||||
__tablename__ = 'sesiones'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), primary_key=True)
|
||||
|
||||
identidadid = db.Column(db.BigInteger(), db.ForeignKey('usuarios.identidades.id'), nullable=False)
|
||||
identidad = db.relationship('Identidad', back_populates='sesiones')
|
||||
|
||||
dispositivoid = db.Column(db.Integer, db.ForeignKey('registros.dispositivos.id'), nullable=False)
|
||||
dispositivo = db.relationship('Dispositivo')
|
||||
|
||||
conexiones = db.relationship('Conexion')
|
||||
|
||||
iniciada = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||
ultimo = db.Column(db.DateTime(timezone=True), server_onupdate=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return u"Session ('{}','{}','{}')".format(self.identidad.login, self.ipaddr.ipaddr, self.useragent.parsed_ua)
|
||||
|
||||
class Conexion(db.Model):
|
||||
__tablename__ = 'conexiones'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), primary_key=True)
|
||||
|
||||
ipaddrid = db.Column(db.Integer, db.ForeignKey('registros.ipaddrs.id'), nullable=False)
|
||||
ipaddr = db.relationship('Ipaddr')
|
||||
|
||||
sesionid = db.Column(db.Integer, db.ForeignKey('registros.sesiones.id'), nullable=False)
|
||||
sesion = db.relationship('Sesion', back_populates='conexiones', lazy=True)
|
||||
|
||||
iniciada = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||
ultimo = db.Column(db.DateTime(timezone=True), server_onupdate=func.now())
|
||||
|
||||
__table_args__ = (db.UniqueConstraint('ipaddrid', 'sesionid', name='_una_ip_sesion_uc'),{ 'schema': 'registros' })
|
||||
|
||||
class Ubicacion(db.Model):
|
||||
__tablename__ = 'ubicaciones'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), primary_key=True)
|
||||
|
||||
ipaddrid = db.Column(db.Integer, db.ForeignKey('registros.ipaddrs.id'), nullable=False)
|
||||
ipaddr = db.relationship('Ipaddr')
|
||||
|
||||
identidadid = db.Column(db.Integer, db.ForeignKey('usuarios.identidades.id'), nullable=False)
|
||||
identidad = db.relationship('Identidad', back_populates='ubicaciones', lazy=True)
|
||||
|
||||
descripcion = db.Column(db.String(200), nullable=True)
|
||||
|
||||
iniciada = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||
ultimo = db.Column(db.DateTime(timezone=True), server_onupdate=func.now())
|
||||
|
||||
__table_args__ = (db.UniqueConstraint('ipaddrid', 'identidadid', name='_una_ip_identidad_uc'),{ 'schema': 'registros' })
|
||||
|
||||
class Ipaddr(db.Model):
|
||||
__tablename__ = 'ipaddrs'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
ipaddr = db.Column(db.String(15).with_variant(postgresql.INET(), 'postgresql'), unique=True, nullable=False)
|
||||
hostname = db.Column(db.String(100), nullable=True)
|
||||
|
||||
def __repr__(self):
|
||||
return "IpAddr('{}','{}')".format(self.ipaddr, self.hostname)
|
||||
|
||||
class Dispositivo(db.Model):
|
||||
__tablename__ = 'dispositivos'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
useragent = db.Column(db.String(1024), unique=True, nullable=False)
|
||||
parsed_ua = db.Column(db.String(150))
|
||||
dev = db.Column(db.String(50))
|
||||
os = db.Column(db.String(50))
|
||||
browser = db.Column(db.String(50))
|
||||
|
||||
def __repr__(self):
|
||||
return "Useragent('{}')".format(self.parsed_ua)
|
||||
|
||||
class Ruta(db.Model):
|
||||
__tablename__ = 'rutas'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
ruta = db.Column(db.String(100), unique=True, nullable=False)
|
||||
|
||||
def __repr__(self):
|
||||
return "Url('{}')".format(self.ruta)
|
||||
|
||||
class Registro(db.Model):
|
||||
__tablename__ = 'registros'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), primary_key=True)
|
||||
|
||||
sitioid = db.Column(db.Integer, db.ForeignKey('registros.sitios.id'), nullable=False)
|
||||
sitio = db.relationship('Sitio')
|
||||
|
||||
rutaid = db.Column(db.Integer, db.ForeignKey('registros.rutas.id'), nullable=False)
|
||||
ruta = db.relationship('Ruta')
|
||||
|
||||
sesionid = db.Column(db.BigInteger().with_variant(sqlite.INTEGER(), 'sqlite'), db.ForeignKey('registros.sesiones.id'), nullable=False)
|
||||
sesion = db.relationship('Sesion')
|
||||
|
||||
ipaddrid = db.Column(db.Integer, db.ForeignKey('registros.ipaddrs.id'), nullable=False)
|
||||
ipaddr = db.relationship('Ipaddr')
|
||||
|
||||
tamano = db.Column(db.Integer)
|
||||
creado = db.Column(db.DateTime, server_default=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return "Registro('{}')".format(self.ruta.ruta)
|
||||
|
||||
class Sitio(db.Model):
|
||||
__tablename__ = 'sitios'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
sitio = db.Column(db.String(100), unique=True, nullable=False)
|
|
@ -0,0 +1,59 @@
|
|||
# coding: utf-8
|
||||
from flask import Flask
|
||||
from flask.logging import default_handler
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_login import LoginManager
|
||||
from flask_mail import Mail
|
||||
from flask_bcrypt import Bcrypt
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from .config import Config
|
||||
|
||||
#if sys.version_info.major < 3:
|
||||
# reload(sys)
|
||||
#sys.setdefaultencoding('utf8')
|
||||
|
||||
db = SQLAlchemy()
|
||||
bcrypt = Bcrypt()
|
||||
mail = Mail()
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.login_view = 'main.login'
|
||||
login_manager.login_message = u'Para continuar, ingrese su nombre de usuario y contraseña.'
|
||||
login_manager.login_message_category = 'info'
|
||||
|
||||
|
||||
|
||||
|
||||
def create_app(config_class=Config):
|
||||
app = Flask(__name__)
|
||||
|
||||
app.config.from_object(config_class)
|
||||
|
||||
if app.debug:
|
||||
app.logger.setLevel(logging.DEBUG)
|
||||
else:
|
||||
app.logger.setLevel(logging.INFO)
|
||||
|
||||
db.init_app(app)
|
||||
login_manager.init_app(app)
|
||||
mail.init_app(app)
|
||||
|
||||
|
||||
from webinterface.content.errors.handlers import errors
|
||||
app.register_blueprint(errors)
|
||||
|
||||
from webinterface.content.main import main
|
||||
app.register_blueprint(main)
|
||||
|
||||
from webinterface.content.gestion.routes import gestion
|
||||
app.register_blueprint(gestion)
|
||||
|
||||
from webinterface.content.gtfs.routes import gtfs
|
||||
app.register_blueprint(gtfs)
|
||||
|
||||
from webinterface.content.docs.routes import docs
|
||||
app.register_blueprint(docs)
|
||||
|
||||
return app
|
|
@ -0,0 +1,19 @@
|
|||
import os
|
||||
|
||||
class Config:
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY')
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
SQLALCHEMY_ECHO = False
|
||||
DEBUG = os.environ.get('DEBUG')
|
||||
SESSION_COOKIE_DOMAIN = os.environ.get('COOKIE_DOMAIN')
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_WEBDB_URI')
|
||||
|
||||
SQLALCHEMY_BINDS = {
|
||||
'system': os.environ.get('SQLALCHEMY_WEBDB_URI'),
|
||||
'gtfs_work': os.environ.get('SQLALCHEMY_HOTDB_URI'),
|
||||
'gtfs_static': os.environ.get('SQLALCHEMY_GTFSDB_URI'),
|
||||
}
|
||||
MAIL_DEBUG = int(os.environ.get('DEBUG') == True)
|
||||
|
||||
MAIL_SERVER = os.environ.get('MAIL_SERVER')
|
||||
MAIL_PORT = os.environ.get('MAIL_PORT')
|
|
@ -0,0 +1,89 @@
|
|||
# coding: utf-8
|
||||
from flask import send_file, render_template, flash, redirect, url_for, request, Blueprint, current_app, abort, g
|
||||
from flask_login import login_required, current_user
|
||||
from webinterface import db
|
||||
from webinterface.models.documentos import Documento
|
||||
from webinterface.models.gestion import Evidencia, AnexosActa
|
||||
from .utils import ingest_file
|
||||
from werkzeug.utils import secure_filename
|
||||
import os
|
||||
|
||||
|
||||
docs = Blueprint('docs', __name__)
|
||||
|
||||
@docs.before_request
|
||||
@login_required
|
||||
def verifica_permisos():
|
||||
pass
|
||||
|
||||
def touchContainer(filehash, oldhash):
|
||||
try:
|
||||
|
||||
if 'actividadid' in request.form and int(request.form['actividadid']) > 0:
|
||||
actividadid = request.form['actividadid']
|
||||
current_app.logger.debug(u'Actividad {} detectada' .format(actividadid))
|
||||
|
||||
inode = Evidencia.query.filter(Evidencia.actividadid==actividadid, Evidencia.documentohash==oldhash).one_or_none()
|
||||
if inode is not None:
|
||||
db.session.delete(inode)
|
||||
|
||||
inode = Evidencia.query.filter(Evidencia.actividadid==actividadid, Evidencia.documentohash==filehash).one_or_none()
|
||||
if inode is None:
|
||||
inode = Evidencia(actividadid=actividadid, documentohash=filehash)
|
||||
db.session.add(inode)
|
||||
db.session.commit()
|
||||
return inode
|
||||
|
||||
elif 'actaid' in request.form and int(request.form['actaid']) > 0:
|
||||
actaid = request.form['actaid']
|
||||
current_app.logger.debug(u'AnexosActa {} detectado' .format(actaid))
|
||||
|
||||
inode = AnexosActa.query.filter(AnexosActa.actaid==actaid, AnexosActa.documentohash==oldhash).one_or_none()
|
||||
if inode is not None:
|
||||
db.session.delete(inode)
|
||||
|
||||
inode = AnexosActa.query.filter(AnexosActa.actaid==actaid, AnexosActa.documentohash==filehash).one_or_none()
|
||||
if inode is None:
|
||||
inode = AnexosActa(actaid=actaid, documentohash=filehash)
|
||||
db.session.add(inode)
|
||||
db.session.commit()
|
||||
return inode
|
||||
else:
|
||||
return None
|
||||
except:
|
||||
db.session.rollback()
|
||||
return None
|
||||
|
||||
|
||||
@docs.route("/docs/upload", methods=['POST'])
|
||||
def upload():
|
||||
(item, previtem) = ingest_file()
|
||||
|
||||
if item is None or (previtem is not None and touchContainer(item.hash, previtem.hash) is None) or (previtem is None and touchContainer(item.hash, "00000000000000000000000000000000") is None):
|
||||
current_app.logger.debug(u'Item is {}, previtem is {}'.format(item, previtem))
|
||||
flash(u"Ocurrió un error al subir el documento", "danger")
|
||||
else:
|
||||
flash(u"El documento fue almacenado con éxito", "success")
|
||||
nombre, extension = os.path.splitext(item.nombre.lower())
|
||||
if extension not in ['.pdf', '.jpg', '.jpeg', '.png']:
|
||||
flash(u"El formato '{}' no es compatible con los reportes automáticos. Si desea que se incluya como evidencia en los reportes súbalo como PDF (tamaño carta) o imágen compatible.".format(extension), "warning")
|
||||
|
||||
return redirect(request.form['redirect'])
|
||||
|
||||
|
||||
@docs.route("/docs/<string:filehash>")
|
||||
def get(filehash=0):
|
||||
doc = Documento.query.get(filehash)
|
||||
|
||||
if doc.tamano == 0:
|
||||
return redirect(doc.nombre)
|
||||
else:
|
||||
try:
|
||||
return send_file(os.path.abspath(doc.get_file()), as_attachment=True, download_name=doc.nombre, last_modified=doc.creado)
|
||||
except FileNotFoundError:
|
||||
abort(404)
|
||||
|
||||
|
||||
@docs.route("/docs/evidencia", methods=['POST'])
|
||||
def evidencias():
|
||||
return upload()
|
|
@ -0,0 +1,102 @@
|
|||
# coding: utf-8
|
||||
from flask import send_file, render_template, flash, redirect, url_for, request, Blueprint, current_app, abort, g
|
||||
from flask_login import login_required, current_user
|
||||
from webinterface import db
|
||||
from webinterface.models.documentos import Documento
|
||||
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
def ingest_file():
|
||||
import datetime
|
||||
import hashlib
|
||||
import os
|
||||
from shutil import copyfile
|
||||
from tempfile import NamedTemporaryFile
|
||||
# for key, values in request.form.items():
|
||||
# current_app.logger.info('request-form: {}: {}'.format(key, values))
|
||||
#
|
||||
# for key, values in request.files.items():
|
||||
# current_app.logger.info('request-file: {}: {}'.format(key, values))
|
||||
|
||||
uuid_nill = "00000000000000000000000000000000"
|
||||
try:
|
||||
if len(request.form['currenthash']) < 32:
|
||||
currenthash = uuid_nill
|
||||
else:
|
||||
currenthash = request.form['currenthash']
|
||||
except Exception as e:
|
||||
currenthash = uuid_nill
|
||||
|
||||
previtem = Documento.query.filter_by(hash=currenthash).one_or_none()
|
||||
|
||||
sha1 = hashlib.sha1()
|
||||
newdocs = None
|
||||
|
||||
if 'documento' in request.files and len(request.files['documento'].filename) > 0:
|
||||
uploaded_file = request.files['documento']
|
||||
# current_app.logger.info('Archivo subido: procesando {}'.format(uploaded_file.filename))
|
||||
fsize = 0
|
||||
with NamedTemporaryFile() as tmp:
|
||||
try:
|
||||
BUF_SIZE = 65536
|
||||
uploaded_file.save(tmp.name)
|
||||
fsize = os.path.getsize(tmp.name)
|
||||
# current_app.logger.debug('Archivo subido: Tamaño {} bytes'.format(fsize))
|
||||
with open(tmp.name, 'rb') as f:
|
||||
while True:
|
||||
data = f.read(BUF_SIZE)
|
||||
if not data:
|
||||
break
|
||||
sha1.update(data)
|
||||
# current_app.logger.debug('Archivo subido: hash {}'.format(sha1.hexdigest()[:32]))
|
||||
|
||||
except Exception as e:
|
||||
# current_app.logger.error('Error leyendo el archivo: Name({}), Error({})'.format(uploaded_file.filename, str(e)))
|
||||
return (None, None)
|
||||
else:
|
||||
item = Documento.query.filter_by(hash=sha1.hexdigest()[:32]).one_or_none()
|
||||
if item is None:
|
||||
name = secure_filename(uploaded_file.filename)
|
||||
item = Documento(hash=sha1.hexdigest()[:32], nombre=name, descripcion=request.form['descripcion'].strip(), tamano=fsize, autorid=current_user.id, reemplazahash=previtem.hash)
|
||||
db.session.add(item)
|
||||
db.session.commit()
|
||||
try:
|
||||
# current_app.logger.debug('Directorio: {}'.format(os.path.abspath(item.get_dir())))
|
||||
os.makedirs(os.path.abspath(item.get_dir()), exist_ok=True)
|
||||
# current_app.logger.debug('Archivo: {}'.format(os.path.abspath(item.get_file())))
|
||||
copyfile(tmp.name, os.path.abspath(item.get_file()))
|
||||
|
||||
except Exception as e:
|
||||
return (None, None)
|
||||
else:
|
||||
if not os.path.exists(os.path.abspath(item.get_file())):
|
||||
try:
|
||||
# current_app.logger.info('Archivo: {}'.format(os.path.abspath(item.get_file())))
|
||||
item.name = secure_filename(uploaded_file.filename)
|
||||
item.reemplazahash = previtem.hash
|
||||
copyfile(tmp.name, os.path.abspath(item.get_file()))
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
current_app.logger.error('Error al guardar el archivo: Name({}), Error({})'.format(uploaded_file.filename, str(e)))
|
||||
return (None, None)
|
||||
|
||||
# current_app.logger.info('Archivo subido: guardado {}'.format(item.get_file()))
|
||||
elif previtem.tamano == 0 and len(request.form['url']) > 5:
|
||||
sha1.update(request.form['url'].strip().encode('utf-8'))
|
||||
item = Documento.query.filter_by(hash=sha1.hexdigest()[:32]).one_or_none()
|
||||
if item is None:
|
||||
item = Documento(hash=sha1.hexdigest()[:32], nombre=request.form['url'].strip(), descripcion=request.form['descripcion'].strip(), tamano=0, autorid=current_user.id, reemplazahash=previtem.hash)
|
||||
db.session.add(item)
|
||||
else:
|
||||
item.descripcion=request.form['descripcion'].strip()
|
||||
db.session.commit()
|
||||
|
||||
elif currenthash != uuid_nill:
|
||||
previtem.descripcion = request.form['descripcion']
|
||||
db.session.commit()
|
||||
return (previtem, None)
|
||||
else:
|
||||
return (None, None)
|
||||
|
||||
return (item, previtem)
|
|
@ -0,0 +1,22 @@
|
|||
from flask import Blueprint, render_template
|
||||
from flask_login import login_required
|
||||
errors = Blueprint('errors', __name__)
|
||||
|
||||
|
||||
@errors.app_errorhandler(404)
|
||||
@login_required
|
||||
def error_404(error):
|
||||
return render_template('errors/404.html'), 404
|
||||
|
||||
|
||||
@errors.app_errorhandler(403)
|
||||
@login_required
|
||||
def error_403(error):
|
||||
return render_template('errors/403.html'), 403
|
||||
|
||||
|
||||
@errors.app_errorhandler(500)
|
||||
def error_500(error):
|
||||
return render_template('errors/500.html'), 500
|
||||
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
# coding: utf-8
|
||||
from flask import send_file, render_template, flash, redirect, url_for, request, Blueprint, current_app, abort, g
|
||||
from flask_login import login_required, current_user
|
||||
from ilab_app import db
|
||||
from ilab_app.models.gestion import Evidencia, Documento, Actividad, Objetivo
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
|
||||
evidencia = Blueprint('evidencia', __name__)
|
||||
|
||||
@evidencia.before_request
|
||||
@login_required
|
||||
def verifica_permisos():
|
||||
pass
|
||||
|
||||
@evidencia.route("/evidencia/subir", methods=['POST'])
|
||||
def subir():
|
||||
import datetime
|
||||
import hashlib
|
||||
for key, values in request.form.items():
|
||||
current_app.logger.debug('request-form: {}: {}'.format(key, values))
|
||||
|
||||
for key, values in request.files.items():
|
||||
current_app.logger.debug('request-file: {}: {}'.format(key, values))
|
||||
|
||||
uuid_nill = "00000000000000000000000000000000"
|
||||
try:
|
||||
if len(request.form['currenthash']) < 32:
|
||||
currenthash = uuid_nill
|
||||
else:
|
||||
currenthash = request.form['currenthash']
|
||||
except Exception as e:
|
||||
currenthash = uuid_nill
|
||||
|
||||
previtem = Documento.query.filter_by(hash=currenthash).one_or_none()
|
||||
item = None
|
||||
sha1 = hashlib.sha1()
|
||||
|
||||
|
||||
if 'documento' in request.files and len(request.files['documento'].filename) > 0:
|
||||
from tempfile import NamedTemporaryFile
|
||||
import os
|
||||
uploaded_file = request.files['documento']
|
||||
current_app.logger.debug('Archivo subido: procesando {}'.format(uploaded_file.filename))
|
||||
fsize = 0
|
||||
with NamedTemporaryFile() as tmp:
|
||||
try:
|
||||
BUF_SIZE = 65536
|
||||
uploaded_file.save(tmp.name)
|
||||
fsize = os.path.getsize(tmp.name)
|
||||
current_app.logger.debug('Archivo subido: Tamaño {} bytes'.format(fsize))
|
||||
with open(tmp.name, 'rb') as f:
|
||||
while True:
|
||||
data = f.read(BUF_SIZE)
|
||||
if not data:
|
||||
break
|
||||
sha1.update(data)
|
||||
current_app.logger.debug('Archivo subido: hash {}'.format(sha1.hexdigest()[:32]))
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error('Error leyendo el archivo: Name({}), Error({})'.format(uploaded_file.filename, str(e)))
|
||||
item = None
|
||||
else:
|
||||
item = Documento.query.filter_by(hash=sha1.hexdigest()[:32]).one_or_none()
|
||||
if item is None:
|
||||
name = secure_filename(uploaded_file.filename)
|
||||
item = Documento(hash=sha1.hexdigest()[:32], nombre=name, descripcion=request.form['descripcion'].strip(), tamano=fsize, autorid=current_user.id, reemplazahash=previtem.hash)
|
||||
db.session.add(item)
|
||||
|
||||
actid = int(request.form['aid'])
|
||||
newevidencia = Evidencia(actividadid=actid, documentohash=item.hash)
|
||||
db.session.add(newevidencia)
|
||||
db.session.commit()
|
||||
|
||||
try:
|
||||
from shutil import copyfile
|
||||
current_app.logger.debug('Directorio: {}'.format(os.path.abspath(item.get_dir())))
|
||||
os.makedirs(os.path.abspath(item.get_dir()))
|
||||
current_app.logger.debug('Archivo: {}'.format(os.path.abspath(item.get_file())))
|
||||
copyfile(tmp.name, os.path.abspath(item.get_file()))
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error('Error al guardar el archivo: Name({}), Error({})'.format(uploaded_file.filename, str(e)))
|
||||
item = None
|
||||
|
||||
else:
|
||||
current_app.logger.debug('Archivo subido: guardado {}'.format(item.get_file()))
|
||||
else:
|
||||
item.descripcion=request.form['descripcion'].strip()
|
||||
db.session.commit()
|
||||
|
||||
elif previtem.tamano == 0 and len(request.form['url']) > 5:
|
||||
sha1.update(request.form['url'].strip().encode('utf-8'))
|
||||
item = Documento.query.filter_by(hash=sha1.hexdigest()[:32]).one_or_none()
|
||||
if item is None:
|
||||
item = Documento(hash=sha1.hexdigest()[:32], nombre=request.form['url'].strip(), descripcion=request.form['descripcion'].strip(), tamano=0, autorid=current_user.id, reemplazahash=previtem.hash)
|
||||
db.session.add(item)
|
||||
|
||||
actid = int(request.form['aid'])
|
||||
newevidencia = Evidencia(actividadid=actid, documentohash=item.hash)
|
||||
db.session.add(newevidencia)
|
||||
|
||||
db.session.commit()
|
||||
else:
|
||||
item.descripcion=request.form['descripcion'].strip()
|
||||
db.session.commit()
|
||||
elif currenthash != uuid_nill:
|
||||
previtem.descripcion = request.form['descripcion']
|
||||
db.session.commit()
|
||||
item = None
|
||||
|
||||
if item != None and item.hash != previtem.hash and not previtem.placeholder:
|
||||
from ilab_app.models.system import get_comisiones
|
||||
actid = int(request.form['aid'])
|
||||
|
||||
comisiones = get_comisiones()
|
||||
|
||||
evidencias = Evidencia.query.filter(Evidencia.documentohash==previtem.hash).\
|
||||
join(Evidencia.actividad).join(Actividad.objetivo).\
|
||||
filter(Objetivo.estado==0, Objetivo.responsableid.in_(comisiones)).all()
|
||||
|
||||
for evidencia in evidencias:
|
||||
if evidencia.actividadid != actid:
|
||||
newevidencia = Evidencia(actividadid=evidencia.actividadid, documentohash=item.hash)
|
||||
db.session.add(newevidencia)
|
||||
db.session.delete(evidencia)
|
||||
|
||||
db.session.commit()
|
||||
elif item != None and item.hash != previtem.hash:
|
||||
actid = int(request.form['aid'])
|
||||
prevev = Evidencia.query.filter_by(actividadid=actid, documentohash=previtem.hash).one_or_none()
|
||||
if prevev != None:
|
||||
db.session.delete(prevev)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for(request.form['redirect']))
|
||||
|
||||
|
||||
@evidencia.route("/evidencia/<string:filehash>")
|
||||
def get(filehash=0):
|
||||
from ilab_app.models.system import get_comisiones
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy.orm import aliased
|
||||
|
||||
Hijo = aliased(Objetivo)
|
||||
|
||||
comisiones = get_comisiones()
|
||||
|
||||
cani = Objetivo.query.join(Hijo, Objetivo.hijos).\
|
||||
filter(or_(Objetivo.responsableid.in_(comisiones), Hijo.responsableid.in_(comisiones), Hijo.invitadosid.in_(comisiones))).\
|
||||
join(Hijo.actividades).join(Actividad.evidencias).\
|
||||
filter(Evidencia.documentohash==filehash).first()
|
||||
|
||||
if cani is None:
|
||||
abort(403)
|
||||
else:
|
||||
doc = Documento.query.get(filehash)
|
||||
if doc.placeholder:
|
||||
return render_template('system/placeholder.html', doc=doc)
|
||||
elif doc.tamano == 0:
|
||||
return redirect(doc.nombre)
|
||||
else:
|
||||
try:
|
||||
import os
|
||||
return send_file(os.path.abspath(doc.get_file()), as_attachment=True, download_name=doc.nombre, last_modified=doc.creado)
|
||||
except FileNotFoundError:
|
||||
abort(404)
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,735 @@
|
|||
# coding: utf-8
|
||||
import os
|
||||
import math
|
||||
from PIL import Image
|
||||
from fpdf import FPDF
|
||||
from flask import current_app
|
||||
from flask_login import current_user
|
||||
from tempfile import NamedTemporaryFile
|
||||
from functools import cache
|
||||
from webinterface.models.documentos import Documento
|
||||
from datetime import datetime, timedelta, date
|
||||
from PyPDF2 import PdfFileReader, PdfFileMerger
|
||||
from webinterface.models.gestion import Objetivo, Accion, Evidencia, Comision
|
||||
|
||||
@cache
|
||||
def logoyencabezado(Nodo):
|
||||
uuid_nill = "00000000000000000000000000000000"
|
||||
responsable = Nodo.responsable
|
||||
|
||||
while responsable.creado > 1:
|
||||
responsable = responsable.supervisor
|
||||
|
||||
titulo = None
|
||||
logo = None
|
||||
docLogo = Documento.query.filter(Documento.hash==responsable.logo).one_or_none()
|
||||
if docLogo is not None:
|
||||
logoFile = docLogo.get_file()
|
||||
if logoFile is not None:
|
||||
logo = os.path.abspath(logoFile)
|
||||
|
||||
docTitulo = Documento.query.filter(Documento.hash==responsable.titulo).one_or_none()
|
||||
if docTitulo is not None:
|
||||
tituloFile = docTitulo.get_file()
|
||||
if tituloFile is not None:
|
||||
titulo = os.path.abspath(tituloFile)
|
||||
|
||||
return (logo, titulo)
|
||||
|
||||
|
||||
def ReportePDF(Archivo, Nodo, Fini=None, Ffin=None):
|
||||
logo, titulo = logoyencabezado(Nodo)
|
||||
|
||||
maindoc = plantillaReporte(Titulo=Nodo.nombre, Logo=logo, Encabezado=titulo)
|
||||
|
||||
maindoc.set_title(u"Reporte de Gestión: {}".format(Nodo.nombre))
|
||||
maindoc.set_subject(Nodo.nombre)
|
||||
maindoc.set_author("iLab Gestion: {}".format(maindoc.serial))
|
||||
maindoc.set_creator(current_user.ascii_nombrecompleto)
|
||||
|
||||
Horachilena=datetime.today() - timedelta(hours=4)
|
||||
Horachilenastr = Horachilena.strftime('Generado el %d/%m/%Y a las %H horas')
|
||||
|
||||
if isinstance(Fini,date) and isinstance(Ffin, date) and Fini < Ffin:
|
||||
maindoc.Tapa('Reporte de Gestión desde {:%d/%m/%Y} hasta {:%d/%m/%Y}.'.format(Fini, Ffin), Linea2=Nodo.nombre, Linea3=Horachilenastr, Logo=True)
|
||||
else:
|
||||
maindoc.Tapa('Reporte de Gestión', Linea2=Nodo.nombre, Linea3=Horachilenastr, Logo=True)
|
||||
|
||||
maindoc, _, _ = ReporteNodo(maindoc, Nodo, True, Fini, Ffin)
|
||||
|
||||
paginas_en_blanco = maindoc.paginas_en_blanco
|
||||
anexos_pdf = maindoc.anexospdf
|
||||
paginas_reporte = maindoc.page_no()
|
||||
|
||||
current_app.logger.debug(u'PDF original tiene {} páginas'.format(paginas_reporte))
|
||||
|
||||
with NamedTemporaryFile() as tmp:
|
||||
maindoc.output(name=tmp.name, dest='F')
|
||||
|
||||
merger = PdfFileMerger(strict=False)
|
||||
pagina = 0
|
||||
while pagina < paginas_reporte:
|
||||
|
||||
if pagina not in paginas_en_blanco:
|
||||
merger.append(tmp.name, pages=(pagina, pagina+1))
|
||||
pagina = pagina + 1
|
||||
else:
|
||||
archivo_pdf, largo = anexos_pdf.pop(0)
|
||||
merger.append(archivo_pdf)
|
||||
pagina = pagina + largo
|
||||
|
||||
merger.write(Archivo)
|
||||
merger.close()
|
||||
|
||||
return "reporte_gestion-{}-{}.pdf".format(Nodo.id, maindoc.serial)
|
||||
|
||||
def ListaActaPDF(Archivo, Acta):
|
||||
logo, titulo = logoyencabezado(Acta.objetivo)
|
||||
|
||||
maindoc = plantillaReporte(Titulo='Acta {}: {}'.format(Acta.secuencia, Acta.objetivo.nombre), Logo=logo, Encabezado=titulo)
|
||||
maindoc.add_page()
|
||||
maindoc.PutLogoCenter()
|
||||
maindoc.Titulo1(u'{} - Acta {}'.format(Acta.objetivo.nombre, Acta.secuencia), align='C', newpage=False)
|
||||
maindoc.Titulo2(u"Listado de Asistentes", align='C', Tight=True)
|
||||
maindoc.Titulo2(u"Fecha: {}".format(Acta.horaini.strftime('%d/%m/%Y')), align='C', Tight=True)
|
||||
|
||||
count = 0
|
||||
w = maindoc.AnchoFirma(Acta.asistentes)
|
||||
|
||||
for asistente in Acta.asistentes:
|
||||
if count>0 and count % 11 == 0:
|
||||
maindoc.add_page()
|
||||
maindoc.ln(25)
|
||||
count = count + 1
|
||||
# maindoc.ln(5)
|
||||
maindoc.Firma("{}) {}".format(count, asistente),Ancho=w)
|
||||
|
||||
|
||||
maindoc.output(name=Archivo, dest='F')
|
||||
|
||||
return "Lista acta {}-{} {}.pdf".format(Acta.secuencia, Acta.objetivo.nombre, maindoc.serial)
|
||||
|
||||
def ActaPDF(Archivo, Acta):
|
||||
logo, titulo = logoyencabezado(Acta.objetivo)
|
||||
maindoc = plantillaReporte(Titulo='Acta {}: {}'.format(Acta.secuencia, Acta.objetivo.nombre), Logo=logo, Encabezado=titulo)
|
||||
maindoc = ReporteActa(maindoc, Acta)
|
||||
|
||||
paginas_en_blanco = maindoc.paginas_en_blanco
|
||||
anexos_pdf = maindoc.anexospdf
|
||||
paginas_reporte = maindoc.page_no()
|
||||
|
||||
current_app.logger.debug(u'PDF original tiene {} páginas'.format(paginas_reporte))
|
||||
|
||||
with NamedTemporaryFile() as tmp:
|
||||
maindoc.output(name=tmp.name, dest='F')
|
||||
|
||||
merger = PdfFileMerger(strict=False)
|
||||
pagina = 0
|
||||
while pagina < paginas_reporte:
|
||||
|
||||
if pagina not in paginas_en_blanco:
|
||||
merger.append(tmp.name, pages=(pagina, pagina+1))
|
||||
pagina = pagina + 1
|
||||
else:
|
||||
archivo_pdf, largo = anexos_pdf.pop(0)
|
||||
merger.append(archivo_pdf)
|
||||
pagina = pagina + largo
|
||||
|
||||
merger.write(Archivo)
|
||||
merger.close()
|
||||
|
||||
return "acta {}-{} {}.pdf".format(Acta.secuencia, Acta.objetivo.nombre, maindoc.serial)
|
||||
|
||||
def ReporteActa(documento, Acta):
|
||||
|
||||
documento.add_page()
|
||||
documento.PutLogoCenter()
|
||||
documento.Titulo1(u'Acta {}'.format(Acta.secuencia), align='C', newpage=False)
|
||||
documento.Titulo1(Acta.objetivo.nombre, align='C', newpage=False)
|
||||
|
||||
documento.LugaryFecha(Acta.lugar, Acta.horaini, Acta.horafin)
|
||||
documento.Asistentes(Acta.asistentes)
|
||||
documento.TituloParrafo(u'Temas', Acta.temas)
|
||||
if len(Acta.desarrollo):
|
||||
documento.TituloParrafo(u'Desarrollo', Acta.desarrollo)
|
||||
if len(Acta.acuerdos):
|
||||
documento.TituloParrafo(u'Acuerdos', Acta.acuerdos)
|
||||
|
||||
links = []
|
||||
fotos = []
|
||||
pdfs = []
|
||||
evidencias = 0
|
||||
for evidencia in Acta.anexos:
|
||||
|
||||
if evidencia.documento.nombre[:7] == 'http://' or evidencia.documento.nombre[:8] == 'https://':
|
||||
# current_app.logger.debug(u'Link detectado: {}'.format(evidencia.documento.nombre))
|
||||
links.append((evidencia.documento.nombre, evidencia.documento.descripcion))
|
||||
evidencias = evidencias + 1
|
||||
|
||||
elif evidencia.documento.tamano > 0:
|
||||
nombre, extension = os.path.splitext(evidencia.documento.nombre.lower())
|
||||
|
||||
if extension in ['.jpg', '.jpeg', '.png']:
|
||||
evidencias = evidencias + 1
|
||||
fotos.append(evidencia)
|
||||
|
||||
if extension in ['.pdf']:
|
||||
evidencias = evidencias + 1
|
||||
pdfs.append(evidencia)
|
||||
|
||||
if evidencias > 0:
|
||||
if len(links) > 0:
|
||||
documento.Titulo2('Anexos')
|
||||
for link in links:
|
||||
documento.Link(link)
|
||||
|
||||
if len(fotos) > 0:
|
||||
documento.FotoEvidencia(fotos)
|
||||
|
||||
if len(pdfs) > 0:
|
||||
largo = 0
|
||||
for evidencia in pdfs:
|
||||
archivo = PdfFileReader(open(evidencia.documento.get_file(), "rb"))
|
||||
largo = largo + archivo.getNumPages()
|
||||
documento.anexospdf = (evidencia.documento.get_file(), archivo.getNumPages())
|
||||
documento.pagina_en_blanco(largo)
|
||||
return documento
|
||||
|
||||
def ReporteNodo(documento, Nodo, Recursive=True, Fini=None, Ffin=None, anexoi=0, anexos={}):
|
||||
documento.Titulo1(Nodo.nombre)
|
||||
|
||||
if isinstance(Fini,date) and isinstance(Ffin, date) and Fini < Ffin:
|
||||
rango = True
|
||||
DTini = datetime(Fini.year, Fini.month, Fini.day, 0, 0, 0)
|
||||
DTfin = datetime(Ffin.year, Ffin.month, Ffin.day, 23, 59, 59)
|
||||
else:
|
||||
rango = False
|
||||
|
||||
if Nodo.sub_HDS < 1:
|
||||
documento.Item(Sujeto='Dedicación al objetivo:',Predicado= u'{} Horas cronológicas'.format(round(Nodo.sub_horas,1)), Ancho=55)
|
||||
else:
|
||||
documento.Item(Sujeto='Dedicación al objetivo:',Predicado= u'{} Horas cronológicas ({} Horas de dedicación semanal)'.format(round(Nodo.sub_horas,1), Nodo.sub_HDS), Ancho=55)
|
||||
|
||||
if len(Nodo.descripcion)>0:
|
||||
documento.Titulo3("Descripción del Objetivo")
|
||||
documento.Parrafo(Nodo.descripcion)
|
||||
|
||||
# indicadores
|
||||
eactas = 0
|
||||
|
||||
for Acta in Nodo.actasr:
|
||||
if rango and (Acta.horaini < DTini or Acta.horaini > DTfin):
|
||||
continue
|
||||
|
||||
if eactas == 0:
|
||||
documento.Titulo2("Actas")
|
||||
documento.Indicador(Glosa="Acta/Lugar", vIni="Fecha", vAct="Hora", vFin="Fin", Titulo=True, ancho=25)
|
||||
|
||||
eactas = eactas + 1
|
||||
documento.Indicador(Glosa="Acta {}: {}".format(Acta.secuencia, Acta.lugar), vIni=Acta.horaini.strftime('%d/%m/%Y'), vAct=Acta.horaini.strftime('%H:%M'), vFin=Acta.horafin.strftime('%H:%M'), ancho=25)
|
||||
|
||||
# indicadores
|
||||
eind = 0
|
||||
for Indicador in Nodo.indicadores:
|
||||
if eind == 0:
|
||||
documento.Titulo2("Indicadores")
|
||||
documento.Indicador(Glosa="Indicador", vIni="Inicial", vAct="Actual", vFin="Meta", Titulo=True)
|
||||
|
||||
eind = eind + 1
|
||||
documento.Indicador(Glosa=Indicador.nombre, vIni=Indicador.inicial, vAct=Indicador.valor, vFin=Indicador.meta)
|
||||
|
||||
# Actividades
|
||||
if Recursive is True:
|
||||
anexos = {}
|
||||
eact = 0
|
||||
for Accion in Nodo.iactividades:
|
||||
if rango and (Accion.fecha < Fini or Accion.fecha > Ffin):
|
||||
continue
|
||||
|
||||
if eact == 0:
|
||||
documento.Titulo2("Actividades Realizadas", border='b', Tight=False)
|
||||
|
||||
eact = eact + 1
|
||||
documento.Titulo2(u"Actividad {}: {}".format(eact, Accion.nombre))
|
||||
documento.Item(Sujeto='Fecha:',Predicado= u'{}'.format(Accion.fecha.strftime('%d/%m/%Y')), Ancho=20)
|
||||
if Accion.dedicacion < 26:
|
||||
documento.Item(Sujeto='Dedicación:',Predicado= u'{} Horas cronológicas'.format(Accion.dedicacion), Ancho=30)
|
||||
else:
|
||||
documento.Item(Sujeto='Dedicación:',Predicado= u'{} Horas cronológicas ({} Horas de dedicación semanal)'.format(Accion.dedicacion, round(Accion.dedicacion/26,2)), Ancho=30)
|
||||
if len(Accion.descripcion)>0:
|
||||
documento.Titulo3(u"Descripción de la Actividad")
|
||||
documento.Parrafo(Accion.descripcion)
|
||||
|
||||
evidencias = 0
|
||||
links = []
|
||||
|
||||
for evidencia in Accion.evidencias:
|
||||
if evidencia.documento.nombre[:7] == 'http://' or evidencia.documento.nombre[:8] == 'https://':
|
||||
links.append((evidencia.documento.nombre, evidencia.documento.descripcion))
|
||||
elif evidencia.documento.tamano > 0:
|
||||
nombre, extension = os.path.splitext(evidencia.documento.nombre.lower())
|
||||
if extension in ['.jpg', '.jpeg', '.png', '.pdf']:
|
||||
evidencias = evidencias + 1
|
||||
|
||||
if evidencias > 0 or len(links) > 0:
|
||||
documento.Titulo3('Evidencias')
|
||||
|
||||
if len(links)>0:
|
||||
for link in links:
|
||||
documento.Link(link)
|
||||
|
||||
if evidencias > 0:
|
||||
if anexoi > 25:
|
||||
v1 = chr(ord('A') + int(anexoi/26) - 1)
|
||||
v2 = chr(ord('A') + anexoi%26)
|
||||
|
||||
v = '{}{}'.format(v1,v2)
|
||||
else:
|
||||
v = chr(ord('A') + anexoi)
|
||||
anexoi = anexoi + 1
|
||||
anexos[v] = Accion
|
||||
documento.Parrafo(u'Ver Anexo {}.'.format(v))
|
||||
|
||||
if Recursive is True:
|
||||
for hijo in Nodo.hijos:
|
||||
documento, anexoi, anexos = ReporteNodo(documento, hijo, Recursive=False, Fini=Fini, Ffin=Ffin, anexoi=anexoi, anexos=anexos)
|
||||
else:
|
||||
return (documento, anexoi, anexos)
|
||||
|
||||
eacta = 0
|
||||
|
||||
for Acta in Nodo.actasr:
|
||||
if rango and (Acta.horaini < DTini or Acta.horaini > DTfin):
|
||||
continue
|
||||
if eacta == 0:
|
||||
eacta = 1
|
||||
documento.Tapa('Anexo Actas')
|
||||
|
||||
documento = ReporteActa(documento, Acta)
|
||||
|
||||
if Recursive is True:
|
||||
for hijo in Nodo.hijos:
|
||||
for Acta in hijo.actas:
|
||||
if rango and (Acta.horaini < DTini or Acta.horaini > DTfin):
|
||||
continue
|
||||
if eacta == 0:
|
||||
eacta = 1
|
||||
documento.Tapa('Anexo Actas')
|
||||
|
||||
documento = ReporteActa(documento, Acta)
|
||||
|
||||
|
||||
for letra, anexo in anexos.items():
|
||||
documento.Tapa('Anexo {}'.format(letra))
|
||||
|
||||
fotos = []
|
||||
pdfs = []
|
||||
|
||||
for evidencia in anexo.evidencias:
|
||||
nombre, extension = os.path.splitext(evidencia.documento.nombre.lower())
|
||||
|
||||
if extension in ['.jpg', '.jpeg', '.png']:
|
||||
fotos.append(evidencia)
|
||||
|
||||
if extension in ['.pdf']:
|
||||
pdfs.append(evidencia)
|
||||
|
||||
if len(fotos) > 0:
|
||||
documento.FotoEvidencia(fotos)
|
||||
|
||||
if len(pdfs) > 0:
|
||||
|
||||
largo = 0
|
||||
for evidencia in pdfs:
|
||||
archivo = PdfFileReader(open(evidencia.documento.get_file(), "rb"))
|
||||
largo = largo + archivo.getNumPages()
|
||||
documento.anexospdf = (evidencia.documento.get_file(), archivo.getNumPages())
|
||||
documento.pagina_en_blanco(largo)
|
||||
|
||||
|
||||
return (documento, anexoi, anexos)
|
||||
|
||||
|
||||
class plantillaReporte(FPDF):
|
||||
|
||||
def __init__(self, Titulo = None, Logo = None, Encabezado = None):
|
||||
super(plantillaReporte, self).__init__('P', 'mm', 'Letter')
|
||||
self.set_compression(True)
|
||||
self.add_font('dejavu', '', fname='/srv/font/DejaVuSerif.ttf', uni=True)
|
||||
self.add_font('dejavu', 'B', fname='/srv/font/DejaVuSerif-Bold.ttf', uni=True)
|
||||
self.add_font('dejavu', 'I', fname='/srv/font/DejaVuSerif-Italic.ttf', uni=True)
|
||||
self.add_font('dejavu', 'BI', fname='/srv/font/DejaVuSerif-BoldItalic.ttf', uni=True)
|
||||
self.__titulo = Titulo
|
||||
self.__ancho = 215.9 # 190 / 185
|
||||
self.__alto = 279.4 # 125 / 120
|
||||
self.__columna = 90
|
||||
self.__limiteT2 = 0.8 * self.__alto
|
||||
self.__limiteT3 = 0.85 * self.__alto
|
||||
self.__codigo = datetime.today().strftime('%Y%m%d%H.%M%S')
|
||||
self.__blanks = []
|
||||
self.__pdf = []
|
||||
self.__subtitulo = None
|
||||
self.__h1 = 14
|
||||
self.__h2 = 12
|
||||
self.__h3 = 10
|
||||
self.__logo = Logo
|
||||
self.__encabezado = Encabezado
|
||||
|
||||
# self.__fecha = datetime.today().strftime('%d/%m/%Y a las %H horas')
|
||||
# self.__titulos = []
|
||||
# self.__ptitulos = []
|
||||
|
||||
# Encabezado
|
||||
def header(self):
|
||||
if self.__titulo is not None and self.page_no() > 1:
|
||||
self.set_y(1)
|
||||
self.cell(0, 5+self.__h1, u'', border='B', ln=1, align='C')
|
||||
if self.__encabezado is not None:
|
||||
imLogo = Image.open(os.path.abspath(self.__encabezado))
|
||||
w, h = imLogo.size
|
||||
|
||||
ancho = self.__h2 * w / h
|
||||
|
||||
_, extension = os.path.splitext(self.__logo)
|
||||
|
||||
self.image(self.__encabezado, x=10, y=6, w=ancho, type=extension[1:])
|
||||
else:
|
||||
self.set_font('dejavu', 'B', self.__h2)
|
||||
self.set_y(5)
|
||||
self.cell(110, self.__h1, self.__titulo, border=0, ln=0, align='L')
|
||||
# if self.__subtitulo is not None:
|
||||
# self.cell(0, 10, self.__subtitulo, border=0, ln=0, align='R')
|
||||
self.ln(self.__h2)
|
||||
|
||||
# Pie de Página
|
||||
def footer(self):
|
||||
# Position at 1.5 cm from bottom
|
||||
if self.page_no() > 1:
|
||||
self.set_y(-15)
|
||||
self.cell(0, 8, u'', border='T', ln=1, align='C')
|
||||
self.set_y(-15)
|
||||
self.set_font('dejavu', '', 3)
|
||||
self.cell(60, self.__h3, u'Versión: {}'.format(self.__codigo), border=0, ln=0, align='L')
|
||||
self.set_font('dejavu', 'I', self.__h3)
|
||||
self.cell(0, self.__h3, u'Página {}'.format(self.page_no()), border=0, ln=0, align='R')
|
||||
|
||||
def pagina_en_blanco(self, Num = 1):
|
||||
for i in range(Num):
|
||||
self.Tapa(u"Esta página fue dejada intencionalmente en blanco")
|
||||
self.__blanks.append(self.page_no()-1)
|
||||
|
||||
@property
|
||||
def anexospdf(self):
|
||||
return self.__pdf
|
||||
|
||||
@anexospdf.setter
|
||||
def anexospdf(self, valor):
|
||||
self.__pdf.append(valor)
|
||||
|
||||
@property
|
||||
def paginas_en_blanco(self):
|
||||
return self.__blanks
|
||||
|
||||
@property
|
||||
def serial(self):
|
||||
return self.__codigo
|
||||
|
||||
def Tapa(self, Titulo, Linea2=None, Linea3=None, Logo=False):
|
||||
if self.page_no() > 1:
|
||||
self.__subtitulo = Titulo
|
||||
|
||||
self.add_page()
|
||||
|
||||
if self.__logo is not None and Logo is True:
|
||||
self.ln(self.__h1)
|
||||
self.ln(self.__h2)
|
||||
self.PutLogoCenter()
|
||||
|
||||
|
||||
if Linea2 or Linea3:
|
||||
self.set_y((self.__alto-40)/2)
|
||||
else:
|
||||
self.set_y((self.__alto-20)/2)
|
||||
|
||||
|
||||
self.set_font('dejavu', 'B', self.__h1+2)
|
||||
self.multi_cell(0, self.__h1, Titulo, border=0, align='C')
|
||||
if Linea2 is not None:
|
||||
self.set_font('dejavu', 'B', self.__h1)
|
||||
self.multi_cell(0, self.__h2, Linea2, border=0, align='C')
|
||||
if Linea3 is not None:
|
||||
self.set_font('dejavu', '', self.__h2)
|
||||
self.multi_cell(0, self.__h3, Linea3, border=0, align='C')
|
||||
|
||||
def PutLogoCenter(self):
|
||||
if self.__logo is not None:
|
||||
yini = self.get_y()
|
||||
|
||||
imLogo = Image.open(os.path.abspath(self.__logo))
|
||||
w, h = imLogo.size
|
||||
|
||||
y = self.__columna * h / w
|
||||
xLogo = (self.__ancho - self.__columna) / 2
|
||||
|
||||
_, extension = os.path.splitext(self.__logo)
|
||||
# current_app.logger.debug(u'Logo {} {} {} {} {}'.format(xLogo,w,h,y,extension))
|
||||
self.image(self.__logo, x=xLogo, y=yini, w=self.__columna, type=extension[1:])
|
||||
|
||||
yfin = yini + y
|
||||
|
||||
self.set_y(yfin)
|
||||
|
||||
def Titulo1(self, Titulo, align='L', newpage=True):
|
||||
if newpage:
|
||||
self.add_page()
|
||||
# self.__titulos.append(Titulo)
|
||||
# self.__ptitulos.append(self.page_no())
|
||||
self.set_font('dejavu', 'B', self.__h1)
|
||||
self.multi_cell(0, self.__h1, Titulo, border=0, align=align)
|
||||
# self.cell(0, self.__h1, Titulo, border=0, ln=0, align=align)
|
||||
# self.ln(self.__h1)
|
||||
|
||||
def Titulo2(self, Titulo, border=0, align='L', Tight=True):
|
||||
if not Tight:
|
||||
self.ln(self.__h3)
|
||||
|
||||
if self.get_y() > self.__limiteT2:
|
||||
self.add_page()
|
||||
self.set_font('dejavu', 'B', self.__h2)
|
||||
self.multi_cell(0, self.__h2, Titulo, border=0, align=align)
|
||||
# self.cell(0, self.__h2, Titulo, border=border, ln=0, align=align)
|
||||
# self.ln(self.__h2)
|
||||
|
||||
def Titulo3(self, Titulo):
|
||||
if self.get_y() > self.__limiteT3:
|
||||
self.add_page()
|
||||
self.set_font('dejavu', 'B', self.__h3)
|
||||
self.multi_cell(0, self.__h3, Titulo, border=0, align='L')
|
||||
|
||||
# self.cell(0, self.__h3, Titulo, border=0, ln=0, align='L')
|
||||
# self.ln(self.__h3)
|
||||
|
||||
def Parrafo(self, Texto):
|
||||
self.Normal()
|
||||
self.multi_cell(0, self.__h3, Texto, align='J')
|
||||
# self.ln(self.__h3)
|
||||
|
||||
def Normal(self):
|
||||
self.set_font('dejavu', '', self.__h3)
|
||||
|
||||
def LimpiaColordeFondo(self):
|
||||
self.set_fill_color(1, 1, 1)
|
||||
def ColordeFondo(self):
|
||||
self.set_fill_color(230, 230, 230)
|
||||
|
||||
def TituloParrafo(self, Titulo, Parrafo):
|
||||
self.set_font('dejavu', 'B', self.__h2)
|
||||
self.cell(0, self.__h2, Titulo, border=0, ln=1, align='L')
|
||||
|
||||
self.ColordeFondo()
|
||||
self.Normal()
|
||||
self.multi_cell(0, self.__h3, Parrafo, align='J', fill=True)
|
||||
self.ln(self.__h3)
|
||||
|
||||
def LugaryFecha(self, lugar, horaini, horafin):
|
||||
self.ColordeFondo()
|
||||
|
||||
self.set_font('dejavu', 'B', self.__h2)
|
||||
self.cell(0, self.__h2, "Lugar", border=0, ln=1, align='L')
|
||||
|
||||
self.set_font('dejavu', '', self.__h2)
|
||||
self.cell(0, self.__h2, lugar, border=0, ln=1, align='L', fill = True)
|
||||
|
||||
self.set_font('dejavu', 'B', self.__h2)
|
||||
self.cell(67, self.__h2, 'Fecha', border=0, ln=0, align='L')
|
||||
self.cell(67, self.__h2, 'Inicio', border=0, ln=0, align='L')
|
||||
self.cell(61, self.__h2, u'Término', border=0, ln=1, align='L')
|
||||
|
||||
self.set_font('dejavu', '', self.__h2)
|
||||
self.cell(61, self.__h2, horaini.strftime('%d/%m/%Y'), border=0, ln=0, align='C', fill = True)
|
||||
self.cell(6, self.__h2, '', border=0, ln=0, align='L')
|
||||
self.cell(61, self.__h2, horaini.strftime('%H:%M'), border=0, ln=0, align='C', fill = True)
|
||||
self.cell(6, self.__h2, '', border=0, ln=0, align='L')
|
||||
self.cell(61, self.__h2, horafin.strftime('%H:%M'), border=0, ln=1, align='C', fill = True)
|
||||
|
||||
def Asistentes(self, asistentes):
|
||||
self.ColordeFondo()
|
||||
|
||||
self.set_font('dejavu', 'B', self.__h2)
|
||||
self.cell(0, self.__h2, "Convocados", border=0, ln=1, align='L')
|
||||
|
||||
count = 0
|
||||
for asistente in asistentes:
|
||||
self.set_font('dejavu', '', self.__h3)
|
||||
rcells = math.ceil(self.get_string_width(asistente)/61)
|
||||
|
||||
if rcells == 1:
|
||||
w = 61
|
||||
elif rcells == 2:
|
||||
w = 128
|
||||
else:
|
||||
w = 195
|
||||
|
||||
if ((count + rcells) % 3 <= count % 3):
|
||||
self.set_font('dejavu', '', self.__h3)
|
||||
self.cell(w, self.__h3, asistente, border=0, ln=1, align='C', fill = True)
|
||||
else:
|
||||
self.set_font('dejavu', '', self.__h3)
|
||||
self.cell(w, self.__h3, asistente, border=0, ln=0, align='C', fill = True)
|
||||
self.cell(6, self.__h3, '', border=0, ln=0, align='L')
|
||||
|
||||
count = count + rcells
|
||||
|
||||
if count == 0:
|
||||
self.set_font('dejavu', '', self.__h3)
|
||||
self.cell(0, self.__h3, 'No existen convocados', border=0, ln=1, align='L')
|
||||
elif count % 3 != 0:
|
||||
self.ln(self.__h2)
|
||||
|
||||
def Indicador(self, Glosa, vIni, vAct, vFin, Titulo = False, ancho=16):
|
||||
if Titulo:
|
||||
self.set_font('dejavu', 'B', self.__h3)
|
||||
else:
|
||||
self.set_font('dejavu', '', self.__h3)
|
||||
|
||||
top = self.get_y()
|
||||
anchoglosa = 195 - 3*ancho
|
||||
self.multi_cell(anchoglosa, self.__h3, str(Glosa), border=1, align='J')
|
||||
bottom = self.get_y()
|
||||
|
||||
if bottom < top: # si el final está en la siguiente página, partimos la celda desde arriba.
|
||||
top = 20
|
||||
|
||||
altura = bottom - top # 125-45 = 80 | 125-36 = 89
|
||||
|
||||
|
||||
|
||||
self.set_y(top)
|
||||
self.set_x(anchoglosa+10)
|
||||
|
||||
# current_app.logger.debug(u'Indicador: top:{} bottom:{} altura:{} anchoglosa:{}'.format(top,bottom,altura,anchoglosa))
|
||||
|
||||
self.cell(ancho, altura, str(vIni), border=1, ln=0, align='C')
|
||||
self.cell(ancho, altura, str(vAct), border=1, ln=0, align='C')
|
||||
self.cell(ancho, altura, str(vFin), border=1, ln=1, align='C')
|
||||
self.set_y(bottom)
|
||||
|
||||
def AnchoFirma(self, asistentes):
|
||||
w = 0
|
||||
self.set_font('dejavu', '', self.__h1)
|
||||
for asistente in asistentes:
|
||||
w = max(w, self.get_string_width(asistente))
|
||||
return w+10
|
||||
|
||||
def Firma(self, Sujeto, Ancho=80, Titulo=False):
|
||||
if Titulo:
|
||||
self.set_font('dejavu', 'B', self.__h1)
|
||||
else:
|
||||
self.set_font('dejavu', '', self.__h1)
|
||||
|
||||
self.cell(Ancho, self.__h1, Sujeto , ln=0, align='L')
|
||||
self.cell(0 , self.__h1, '' , border='B', ln=1)
|
||||
|
||||
def Item(self, Sujeto, Predicado, Ancho=80, borde=0, ln=0, alineacion='L'):
|
||||
self.set_font('dejavu', 'I', self.__h2)
|
||||
self.cell(Ancho, self.__h3, Sujeto, border=borde, ln=0, align=alineacion)
|
||||
self.set_font('dejavu', '', self.__h2)
|
||||
self.cell(0, self.__h3, Predicado, border=borde, ln=1, align=alineacion)
|
||||
|
||||
def Link(self, linktupla):
|
||||
link, descripcion = linktupla
|
||||
|
||||
self.set_font('dejavu', '', self.__h2)
|
||||
self.cell(35, self.__h3, u'Link Externo:', border=0, ln=0, align='L')
|
||||
self.set_text_color(r=0, g=0, b=255)
|
||||
if len(descripcion) > 60:
|
||||
self.cell(0, self.__h3, u"{}...".format(descripcion[:60]), border=0, ln=1, align='L', link=link)
|
||||
elif len(descripcion) > 0:
|
||||
self.cell(0, self.__h3, descripcion, border=0, ln=1, align='L', link=link)
|
||||
elif len(link) > 60:
|
||||
self.cell(0, self.__h3, u"{}...".format(link[:60]), border=0, ln=1, align='L', link=link)
|
||||
else:
|
||||
self.cell(0, self.__h3, link, border=0, ln=1, align='L', link=link)
|
||||
self.set_text_color(r=0, g=0, b=0)
|
||||
|
||||
def FotoEvidencia(self, fotos):
|
||||
self.add_page()
|
||||
|
||||
nfotos = len(fotos)
|
||||
|
||||
filas = int(nfotos/2)
|
||||
sobran = nfotos % 2
|
||||
|
||||
|
||||
seq = 0
|
||||
self.set_font('dejavu', '', self.__h2)
|
||||
|
||||
for fila in range(filas):
|
||||
primera = seq
|
||||
segunda = primera + 1
|
||||
seq = seq + 2
|
||||
|
||||
|
||||
# Obtenemos el tamaño de la imagen
|
||||
im1 = Image.open(os.path.abspath(fotos[primera].documento.get_file()))
|
||||
w1, h1 = im1.size
|
||||
im2 = Image.open(os.path.abspath(fotos[segunda].documento.get_file()))
|
||||
w2, h2 = im2.size
|
||||
#Calculamos la altura a medio espacio
|
||||
y1 = self.__columna * h1 / w1
|
||||
y2 = self.__columna * h2 / w2
|
||||
# Vemos si tenemos suficiente espacio en la hoja para poner ambas imagenes
|
||||
ymax = max(y1, y2)
|
||||
if ymax + self.get_y() > self.__limiteT3:
|
||||
self.add_page()
|
||||
# Guardamos la posición inicial antes de pegar las imagenes y sus captions
|
||||
posicionYinicial = self.get_y()
|
||||
|
||||
## Primera imagen
|
||||
yImagenCentrada = posicionYinicial + ymax - y1
|
||||
xColumna1 = 10
|
||||
_, extension = os.path.splitext(fotos[primera].documento.nombre)
|
||||
self.image(fotos[primera].documento.get_file(), x=xColumna1, y=yImagenCentrada, w=self.__columna, type=extension[1:])
|
||||
|
||||
yImagenCentrada = posicionYinicial + ymax - y2
|
||||
xColumna2 = 25 + self.__columna
|
||||
_, extension = os.path.splitext(fotos[segunda].documento.nombre)
|
||||
self.image(fotos[segunda].documento.get_file(), x=xColumna2, y=yImagenCentrada, w=self.__columna, type=extension[1:])
|
||||
|
||||
# Captions de las imagenes
|
||||
self.set_x(10)
|
||||
self.set_y(posicionYinicial + ymax + self.__h3)
|
||||
self.multi_cell(w=self.__columna, h=self.__h3, txt=fotos[primera].documento.descripcion or fotos[primera].documento.nombre, border=0, align='C')
|
||||
|
||||
y1 = self.get_y()
|
||||
|
||||
self.set_y(posicionYinicial + ymax + self.__h3)
|
||||
self.set_x(xColumna2)
|
||||
self.multi_cell(w=self.__columna, h=self.__h3, txt=fotos[segunda].documento.descripcion or fotos[segunda].documento.nombre, border=0, align='C')
|
||||
|
||||
y2 = self.get_y()
|
||||
|
||||
# Dejamos el cursor bajo ambos captions
|
||||
self.set_y(max(y1,y2))
|
||||
self.ln(self.__h3)
|
||||
|
||||
if sobran > 0:
|
||||
seq = nfotos-1
|
||||
|
||||
im1 = Image.open(os.path.abspath(fotos[seq].documento.get_file()))
|
||||
w1, h1 = im1.size
|
||||
|
||||
y1 = self.__columna * h1 / w1
|
||||
|
||||
if y1 + self.get_y() > self.__limiteT2:
|
||||
self.add_page()
|
||||
# Guardamos la posición inicial antes de pegar las imagenes y sus captions
|
||||
posicionYinicial = self.get_y()
|
||||
|
||||
xColumna1 = 10 + self.__columna / 2
|
||||
_, extension = os.path.splitext(fotos[seq].documento.nombre)
|
||||
self.image(fotos[seq].documento.get_file(), x=xColumna1, y=posicionYinicial, w=self.__columna, type=extension[1:])
|
||||
|
||||
self.set_y(posicionYinicial + y1 + self.__h3)
|
||||
self.set_x(xColumna1)
|
||||
self.multi_cell(w=self.__columna, h=self.__h3, txt=fotos[seq].documento.descripcion or fotos[seq].documento.nombre, border=0, align='C')
|
|
@ -0,0 +1,887 @@
|
|||
# coding: utf-8
|
||||
from flask import render_template, flash, redirect, send_file, url_for, request, Blueprint, current_app, abort, g
|
||||
from flask_login import login_required, current_user
|
||||
from webinterface import db
|
||||
from sqlalchemy.sql import func
|
||||
from webinterface.models.gestion import Objetivo, Comision, Miembro, Avance, Accion, AccesoGestion, ModuloGestion, AccesosModuloGestion, Acta, AnexosActa, MiembrosAsistentes, InvitadosAsistentes
|
||||
import datetime
|
||||
|
||||
gestion = Blueprint('gestion', __name__)
|
||||
|
||||
@gestion.before_request
|
||||
@login_required
|
||||
def verifica_permisos():
|
||||
pass
|
||||
|
||||
# @gestion.route("/gestion/nuevoobjetivo", methods=['POST'])
|
||||
# def newsingle():
|
||||
# cid = int(request.form['responsable'])
|
||||
# fid = int(request.form['id'])
|
||||
# nombre = request.form['nombre'].strip()
|
||||
# if fid > 0 and cid > 0 and nombre:
|
||||
# fobj = Objetivo.query.get(fid)
|
||||
# nobj = Objetivo(nombre=nombre, tipo=fobj.tipo, parentid=fid, estado=0, responsableid=cid)
|
||||
# db.session.add(nobj)
|
||||
# db.session.commit()
|
||||
# flash(u'Elemento "{}" generado con exito.'.format(nombre),'success')
|
||||
# else:
|
||||
# flash(u'Los datos ingresados son inválidos.','danger')
|
||||
#
|
||||
# return redirect(url_for('gestion.manager'))
|
||||
|
||||
@gestion.route("/objetivos/editmodulo", methods=['POST'])
|
||||
def editmodulo():
|
||||
# for key, values in request.form.items():
|
||||
# current_app.logger.debug('request: {}: {}'.format(key, values))
|
||||
|
||||
try:
|
||||
mid = int(request.form['id'])
|
||||
except:
|
||||
mid = 0
|
||||
uri = request.form['uri'].strip().lower()
|
||||
|
||||
if not uri:
|
||||
flash("Los datos ingresados son incorrectos: uri vacio", 'danger')
|
||||
abort(400)
|
||||
|
||||
existeA = ModuloGestion.query.filter(ModuloGestion.id==mid).one_or_none()
|
||||
existeB = ModuloGestion.query.filter(ModuloGestion.uri==uri).one_or_none()
|
||||
|
||||
|
||||
if existeA is None and existeB is None:
|
||||
flash("Creado el modulo: {}".format(request.form['nombre'].strip()), 'success')
|
||||
|
||||
nodobase = Objetivo(nombre=request.form['nombre'].strip(), tipo=request.form['icon'].strip().lower(), descripcion='gestion.{}'.format(uri), parentid=1, estado=0, responsableid=1)
|
||||
db.session.add(nodobase)
|
||||
|
||||
if mid==0:
|
||||
existe = ModuloGestion(nombre=request.form['nombre'].strip(), etiqueta=request.form['etiqueta'].strip().lower().capitalize(), uri=uri, nodobase=nodobase)
|
||||
else:
|
||||
existe = ModuloGestion(id=mid, nombre=request.form['nombre'].strip(), etiqueta=request.form['etiqueta'].strip().lower().capitalize(), uri=uri, nodobase=nodobase)
|
||||
|
||||
|
||||
db.session.add(existe)
|
||||
|
||||
for value in request.form.getlist('comisiones'):
|
||||
acceso = AccesosModuloGestion(comisionid=int(value), modulo=existe)
|
||||
db.session.add(acceso)
|
||||
# current_app.logger.debug('Acceso comision: {}'.format(value))
|
||||
|
||||
db.session.commit()
|
||||
elif existeA is not None:
|
||||
|
||||
flash("Modificada el acceso del modulo: {}".format(existeA.nombre), 'success')
|
||||
|
||||
for exmiembro in AccesosModuloGestion.query.filter(AccesosModuloGestion.moduloid==mid).all():
|
||||
# current_app.logger.debug('quita comision: {}'.format(exmiembro.comisionid))
|
||||
db.session.delete(exmiembro)
|
||||
db.session.commit()
|
||||
|
||||
existeA.nombre = request.form['nombre'].strip()
|
||||
existeA.etiqueta = request.form['etiqueta'].strip().lower().capitalize()
|
||||
existeA.uri = uri
|
||||
existeA.nodobase.nombre = request.form['nombre'].strip()
|
||||
existeA.nodobase.tipo = request.form['icon'].strip().lower()
|
||||
existeA.nodobase.descripcion = 'gestion.{}'.format(uri)
|
||||
|
||||
for value in request.form.getlist('comisiones'):
|
||||
acceso = AccesosModuloGestion(comisionid=int(value), modulo=existeA)
|
||||
db.session.add(acceso)
|
||||
# current_app.logger.debug('Acceso comision: {}'.format(value))
|
||||
|
||||
db.session.commit()
|
||||
existe = existeA
|
||||
elif existeB is not None and mid > 0:
|
||||
|
||||
flash("Modificada el acceso del modulo: {}".format(existeB.nombre), 'success')
|
||||
|
||||
for exmiembro in AccesosModuloGestion.query.filter(AccesosModuloGestion.moduloid==existeB.id).all():
|
||||
# current_app.logger.debug('quita comision: {}'.format(exmiembro.comisionid))
|
||||
db.session.delete(exmiembro)
|
||||
db.session.commit()
|
||||
|
||||
existeB.nombre = request.form['nombre'].strip()
|
||||
existeB.etiqueta = request.form['etiqueta'].strip().lower().capitalize()
|
||||
existeB.id = mid
|
||||
existeB.nodobase.nombre = request.form['nombre'].strip()
|
||||
existeB.nodobase.tipo = request.form['icon'].strip().lower()
|
||||
existeB.nodobase.descripcion = 'gestion.{}'.format(uri)
|
||||
|
||||
for value in request.form.getlist('comisiones'):
|
||||
acceso = AccesosModuloGestion(comisionid=int(value), modulo=existeB)
|
||||
db.session.add(acceso)
|
||||
# current_app.logger.debug('Acceso comision: {}'.format(value))
|
||||
|
||||
db.session.commit()
|
||||
existe = existeB
|
||||
else:
|
||||
flash("Los datos ingresados son incorrectos", 'danger')
|
||||
abort(400)
|
||||
|
||||
############################################################################
|
||||
############# Mantenimieto de las accesos grupales #############
|
||||
############################################################################
|
||||
|
||||
administradores = []
|
||||
conacceso = existe.mvector()
|
||||
|
||||
for item in Comision.query.filter(Comision.id>999, Comision.creado==1).all():
|
||||
administradores.append(item.id)
|
||||
|
||||
|
||||
for administrador in administradores:
|
||||
acceso = Objetivo.query.filter(Objetivo.parentid==existe.nodobase.id, Objetivo.responsableid==administrador).one_or_none()
|
||||
if acceso is None and administrador in conacceso:
|
||||
responsable = Comision.query.get(administrador)
|
||||
acceso = Objetivo(nombre="{} {}".format(request.form['nombre'].strip(), responsable.nombre), descripcion=existe.nodobase.descripcion, tipo=existe.nodobase.tipo, responsableid=administrador, parentid=existe.nodobase.id, estado=0)
|
||||
db.session.add(acceso)
|
||||
elif acceso is not None:
|
||||
acceso.tipo = existe.nodobase.tipo
|
||||
acceso.descripcion = existe.nodobase.descripcion
|
||||
if administrador in conacceso:
|
||||
acceso.estado = 0
|
||||
else:
|
||||
acceso.estado = 100
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('gestion.modulos'))
|
||||
|
||||
@gestion.route("/objetivos/nuevacomision", methods=['POST'])
|
||||
def newcomision():
|
||||
cid = int(request.form['id'])
|
||||
|
||||
if cid == 0:
|
||||
ncomision = Comision(nombre=request.form['nombre'].strip(), creado=int(request.form['admin']))
|
||||
db.session.add(ncomision)
|
||||
for value in request.form.getlist('miembros'):
|
||||
miembro = Miembro(personaid=int(value), comision=ncomision)
|
||||
db.session.add(miembro)
|
||||
# current_app.logger.debug('crea miembro: {}'.format(value))
|
||||
db.session.commit()
|
||||
else:
|
||||
for exmiembro in Miembro.query.filter_by(comisionid=cid).all():
|
||||
# current_app.logger.debug('quita miembro: {}'.format(exmiembro.personaid))
|
||||
db.session.delete(exmiembro)
|
||||
db.session.commit()
|
||||
|
||||
ncomision = Comision.query.get(cid)
|
||||
ncomision.nombre=request.form['nombre'].strip()
|
||||
|
||||
for value in request.form.getlist('miembros'):
|
||||
miembro = Miembro(personaid=int(value), comision=ncomision)
|
||||
db.session.add(miembro)
|
||||
# current_app.logger.debug('agrega miembro: {}'.format(value))
|
||||
|
||||
db.session.commit()
|
||||
|
||||
if ncomision.creado==1 and cid==0:
|
||||
return redirect(url_for('gestion.manager', admin=1))
|
||||
elif ncomision.creado==1 and cid>0:
|
||||
return redirect(url_for('gestion.manager', admin=cid))
|
||||
else:
|
||||
return redirect(url_for('gestion.manager', admin=ncomision.creado))
|
||||
|
||||
|
||||
@gestion.route("/objetivos/delcomision/<int:comisionid>", methods=['GET'])
|
||||
def delcomision(comisionid):
|
||||
|
||||
todel = Comision.query.filter(Comision.id == comisionid).first()
|
||||
if todel is None:
|
||||
abort(404)
|
||||
|
||||
if todel.creado == 1:
|
||||
abort(403)
|
||||
|
||||
canI = Miembro.query.filter(Miembro.personaid==current_user.id, Miembro.comisionid==todel.creado).first()
|
||||
if canI is None:
|
||||
abort(403)
|
||||
|
||||
responsable = todel.creado
|
||||
nombre = todel.nombre
|
||||
|
||||
for item in Objetivo.query.filter(Objetivo.responsableid==todel.id).all():
|
||||
item.responsableid = responsable
|
||||
for item in Objetivo.query.filter(Objetivo.invitadosid==todel.id).all():
|
||||
item.invitadosid = None
|
||||
for item in Miembro.query.filter(Miembro.comisionid==todel.id).all():
|
||||
db.session.delete(item)
|
||||
db.session.delete(todel)
|
||||
db.session.commit()
|
||||
|
||||
flash(u"Comision '{}' eliminada con éxito".format(nombre), 'success')
|
||||
return redirect(url_for('gestion.manager', admin=responsable))
|
||||
|
||||
|
||||
@gestion.route("/objetivos/syscomision", methods=['POST'])
|
||||
def syscomision():
|
||||
cid = int(request.form['id'])
|
||||
new = Comision.query.filter(Comision.id == cid).first()
|
||||
|
||||
if cid == 0:
|
||||
ncomision = Comision(nombre=request.form['nombre'].strip(), creado=1)
|
||||
db.session.add(ncomision)
|
||||
for value in request.form.getlist('miembros'):
|
||||
miembro = Miembro(personaid=int(value), comision=ncomision)
|
||||
db.session.add(miembro)
|
||||
# current_app.logger.debug('crea miembro: {}'.format(value))
|
||||
db.session.commit()
|
||||
elif new is None:
|
||||
ncomision = Comision(id=cid, nombre=request.form['nombre'].strip(), creado=1)
|
||||
db.session.add(ncomision)
|
||||
for value in request.form.getlist('miembros'):
|
||||
miembro = Miembro(personaid=int(value), comision=ncomision)
|
||||
db.session.add(miembro)
|
||||
# current_app.logger.debug('crea miembro: {}'.format(value))
|
||||
db.session.commit()
|
||||
else:
|
||||
for exmiembro in Miembro.query.filter_by(comisionid=cid).all():
|
||||
# current_app.logger.debug('quita miembro: {}'.format(exmiembro.personaid))
|
||||
db.session.delete(exmiembro)
|
||||
db.session.commit()
|
||||
|
||||
ncomision = Comision.query.get(cid)
|
||||
ncomision.nombre=request.form['nombre'].strip()
|
||||
|
||||
for value in request.form.getlist('miembros'):
|
||||
miembro = Miembro(personaid=int(value), comision=ncomision)
|
||||
db.session.add(miembro)
|
||||
# current_app.logger.debug('agrega miembro: {}'.format(value))
|
||||
|
||||
db.session.commit()
|
||||
|
||||
############################################################################
|
||||
############# Mantenimieto del acceso alas tareas personales #############
|
||||
############################################################################
|
||||
for item in Objetivo.query.filter(Objetivo.parentid==2, Objetivo.estado<100).all():
|
||||
item.estado=100
|
||||
|
||||
db.session.commit()
|
||||
personal = []
|
||||
for item in Miembro.query.filter(Miembro.comisionid==2).all():
|
||||
personal.append(item.personaid)
|
||||
|
||||
for personaid in personal:
|
||||
from ilab_app.models.system import Persona
|
||||
theuser = Persona.query.get(personaid)
|
||||
|
||||
comisionpersonal = Comision.query.filter(Comision.nombre=="Personal {}".format(theuser.login), Comision.creado==2).one_or_none()
|
||||
if comisionpersonal is None:
|
||||
comisionpersonal = Comision(nombre="Personal {}".format(theuser.login), creado=2)
|
||||
db.session.add(comisionpersonal)
|
||||
db.session.commit()
|
||||
|
||||
miembrocomisonpersoanal = Miembro.query.filter(Miembro.comisionid==comisionpersonal.id).first()
|
||||
if miembrocomisonpersoanal is None:
|
||||
miembrocomisonpersoanal = Miembro(comisionid=comisionpersonal.id, personaid=personaid)
|
||||
db.session.add(miembrocomisonpersoanal)
|
||||
|
||||
|
||||
objetivopersonal = Objetivo.query.filter(Objetivo.responsableid==comisionpersonal.id, Objetivo.parentid==2).one_or_none()
|
||||
if objetivopersonal is None:
|
||||
objetivopersonal = Objetivo(tipo='user-lock', nombre="{}".format(theuser.nombrecompleto), responsableid=comisionpersonal.id, parentid=2, estado=0)
|
||||
db.session.add(objetivopersonal)
|
||||
else:
|
||||
objetivopersonal.estado=0
|
||||
db.session.commit()
|
||||
|
||||
|
||||
|
||||
return redirect(url_for('gestion.manager', admin=1))
|
||||
|
||||
|
||||
@gestion.route("/gestion/actividad", methods=['POST'])
|
||||
def actividad():
|
||||
# for key, values in request.form.items():
|
||||
# current_app.logger.debug('request: {}: {}'.format(key, values))
|
||||
|
||||
try:
|
||||
oid = int(request.form['oid'])
|
||||
pid = int(request.form['pid'])
|
||||
fecha = datetime.datetime.strptime(request.form['fecha'], '%d/%m/%Y').date()
|
||||
except Exception as e:
|
||||
flash("Los datos ingresados son incorrectos: {}".format(str(e)), 'danger')
|
||||
abort(400)
|
||||
try:
|
||||
dedicacion = int(request.form['dedicacion'])
|
||||
except Exception as e:
|
||||
dedicacion = 0
|
||||
|
||||
|
||||
if oid > 0:
|
||||
item = Accion.query.get(oid)
|
||||
|
||||
item.nombre = request.form['nombre'].strip()[:100]
|
||||
item.descripcion = request.form['descripcion'].strip()
|
||||
item.dedicacion = dedicacion
|
||||
item.fecha = fecha
|
||||
|
||||
item.modificadopor = current_user.id
|
||||
# item.modificado = datetime.datetime.now()
|
||||
|
||||
db.session.commit()
|
||||
elif pid > 0:
|
||||
item = Accion(objetivoid=pid, nombre=request.form['nombre'].strip()[:100], descripcion=request.form['descripcion'].strip(),
|
||||
fecha=fecha, estado=0, dedicacion=dedicacion)
|
||||
|
||||
item.creadopor = current_user.id
|
||||
# item.creado = datetime.datetime.now()
|
||||
item.modificadopor = current_user.id
|
||||
# item.modificado = datetime.datetime.now()
|
||||
|
||||
db.session.add(item)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('gestion.show', objetivoid=item.objetivoid))
|
||||
|
||||
@gestion.route("/gestion/acta", methods=['POST'])
|
||||
def acta():
|
||||
# for key, values in request.form.items():
|
||||
# current_app.logger.debug('request: {}: {}'.format(key, values))
|
||||
|
||||
try:
|
||||
aid = int(request.form['aid'])
|
||||
pid = int(request.form['pid'])
|
||||
except Exception as e:
|
||||
import traceback
|
||||
current_app.logger.error('Traceback {}'.format(traceback.format_exc()))
|
||||
flash("Los datos ingresados son incorrectos: {}".format(str(e)), 'danger')
|
||||
abort(400)
|
||||
|
||||
try:
|
||||
lugar = request.form['lugar'].strip()
|
||||
d, m, y = request.form['fecha'].split('/',2)
|
||||
hi, mi = request.form['hini'].split(':',2)
|
||||
hf, mf = request.form['hfin'].split(':',2)
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
current_app.logger.error('Traceback {}'.format(traceback.format_exc()))
|
||||
abort(400)
|
||||
|
||||
horaini = datetime.datetime.strptime('{}/{}/{} {}:{}'.format(d,m,y,hi,mi), "%d/%m/%Y %H:%M")
|
||||
horafin = datetime.datetime.strptime('{}/{}/{} {}:{}'.format(d,m,y,hf,mf), "%d/%m/%Y %H:%M")
|
||||
|
||||
if aid > 0:
|
||||
item = Acta.query.filter(Acta.id==aid).one()
|
||||
|
||||
item.lugar = lugar
|
||||
item.horaini = horaini
|
||||
item.horafin = horafin
|
||||
|
||||
item.temas = request.form['temario']
|
||||
item.desarrollo = request.form['desarrollo']
|
||||
item.acuerdos = request.form['acuerdos']
|
||||
|
||||
item.modificadopor = current_user.id
|
||||
# item.modificado = datetime.datetime.now()
|
||||
|
||||
# db.session.commit()
|
||||
|
||||
elif pid > 0:
|
||||
cantidad = db.session.query(func.count(Acta.id)).filter(Acta.objetivoid==pid).scalar()
|
||||
current_app.logger.debug('Contador {}'.format(cantidad))
|
||||
seq = 1+int(cantidad)
|
||||
|
||||
item = Acta(objetivoid=pid, secuencia=seq, lugar=lugar, horaini=horaini, horafin=horafin, temas=request.form['temario'], desarrollo=request.form['desarrollo'], acuerdos=request.form['acuerdos'])
|
||||
item.creadopor = current_user.id
|
||||
# item.creado = datetime.datetime.now()
|
||||
item.modificadopor = current_user.id
|
||||
# item.modificado = datetime.datetime.now()
|
||||
db.session.add(item)
|
||||
else:
|
||||
current_app.logger.error('aid = 0 y pid = 0')
|
||||
abort(400)
|
||||
|
||||
db.session.commit()
|
||||
miembros = []
|
||||
for ma in MiembrosAsistentes.query.filter(MiembrosAsistentes.actaid==item.id).all():
|
||||
miembros.append(ma.personaid)
|
||||
|
||||
for value in request.form.getlist('asistentes'):
|
||||
v = int(value)
|
||||
if v in miembros:
|
||||
miembros.remove(v)
|
||||
else:
|
||||
db.session.add( MiembrosAsistentes(personaid=v, actaid=item.id) )
|
||||
|
||||
for v in miembros:
|
||||
db.session.delete( MiembrosAsistentes.query.filter(MiembrosAsistentes.actaid==item.id, MiembrosAsistentes.personaid==v).one() )
|
||||
|
||||
invitados = []
|
||||
for ia in InvitadosAsistentes.query.filter(InvitadosAsistentes.actaid==item.id).all():
|
||||
invitados.append(ia.correo)
|
||||
|
||||
for value in request.form['invitados'].splitlines():
|
||||
value = value.replace('\t', ' ')
|
||||
try:
|
||||
correo, nombre = value.strip().split(' ', 1)
|
||||
except:
|
||||
correo = value.strip()
|
||||
nombre = ''
|
||||
|
||||
kcorreo = correo.strip().lower()
|
||||
|
||||
if kcorreo in invitados:
|
||||
invitados.remove(kcorreo)
|
||||
ikc = InvitadosAsistentes.query.filter(InvitadosAsistentes.actaid==item.id, InvitadosAsistentes.correo==kcorreo).one()
|
||||
ikc.nombre = nombre.strip().title()
|
||||
else:
|
||||
db.session.add( InvitadosAsistentes(correo=kcorreo, actaid=item.id, nombre=nombre.strip().title()) )
|
||||
|
||||
for v in invitados:
|
||||
db.session.delete( InvitadosAsistentes.query.filter(InvitadosAsistentes.actaid==item.id, InvitadosAsistentes.correo==v).one() )
|
||||
|
||||
db.session.commit()
|
||||
flash(u"El Acta fue ingresado con éxito con el correlativo: {}".format(item.secuencia), 'success')
|
||||
|
||||
return redirect(url_for('gestion.show', objetivoid=item.objetivoid))
|
||||
|
||||
|
||||
|
||||
@gestion.route("/gestion/carpeta", methods=['POST'])
|
||||
def carpeta():
|
||||
# for key, values in request.form.items():
|
||||
# current_app.logger.debug('request: {}: {}'.format(key, values))
|
||||
|
||||
try:
|
||||
oid = int(request.form['oid'])
|
||||
pid = int(request.form['pid'])
|
||||
except Exception as e:
|
||||
flash("Los datos ingresados son incorrectos: {}".format(str(e)), 'danger')
|
||||
abort(400)
|
||||
try:
|
||||
dedicacion = int(request.form['dedicacion'])
|
||||
except Exception as e:
|
||||
dedicacion = 0
|
||||
|
||||
fecha = datetime.datetime.strptime(request.form['fecha'], '%d/%m/%Y').date()
|
||||
|
||||
if oid > 0:
|
||||
item = Accion.query.get(oid)
|
||||
|
||||
item.nombre = request.form['nombre'].strip()[:100]
|
||||
item.descripcion = request.form['descripcion'].strip()
|
||||
item.dedicacion = dedicacion
|
||||
item.fecha = fecha
|
||||
|
||||
item.modificadopor = current_user.id
|
||||
# item.modificado = datetime.datetime.now()
|
||||
|
||||
|
||||
db.session.commit()
|
||||
elif pid > 0:
|
||||
item = Accion(objetivoid=pid, nombre=request.form['nombre'].strip()[:100], descripcion=request.form['descripcion'].strip(),
|
||||
fecha=fecha, estado=0, dedicacion=dedicacion)
|
||||
|
||||
item.creadopor = current_user.id
|
||||
# item.creado = datetime.datetime.now()
|
||||
item.modificadopor = current_user.id
|
||||
# item.modificado = datetime.datetime.now()
|
||||
|
||||
db.session.add(item)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('gestion.documentos'))
|
||||
|
||||
|
||||
@gestion.route("/gestion/edit", methods=['POST'])
|
||||
def modificar():
|
||||
# for key, values in request.form.items():
|
||||
# current_app.logger.debug('request: {}: {}'.format(key, values))
|
||||
|
||||
try:
|
||||
oid = int(request.form['oid'])
|
||||
pid = int(request.form['pid'])
|
||||
rid = int(request.form['responsable'])
|
||||
except Exception as e:
|
||||
flash("Los datos ingresados son incorrectos: {}".format(str(e)), 'danger')
|
||||
abort(400)
|
||||
|
||||
try:
|
||||
iid = int(request.form['invitado'])
|
||||
except:
|
||||
iid = None
|
||||
finally:
|
||||
if iid == 0:
|
||||
iid = None
|
||||
|
||||
if oid > 0:
|
||||
item = Objetivo.query.get(oid)
|
||||
|
||||
item.nombre = request.form['nombre'].strip()[:200]
|
||||
item.descripcion = request.form['descripcion'].strip()
|
||||
item.responsableid = rid
|
||||
item.invitadosid = iid
|
||||
|
||||
item.modificadopor = current_user.id
|
||||
# item.modificado = datetime.datetime.now()
|
||||
|
||||
db.session.commit()
|
||||
elif pid > 0:
|
||||
item = Objetivo(parentid=pid, nombre=request.form['nombre'].strip()[:200], descripcion = request.form['descripcion'].strip(),
|
||||
responsableid = rid, invitadosid=iid, estado=0)
|
||||
|
||||
item.creadopor = current_user.id
|
||||
item.modificadopor = current_user.id
|
||||
# item.creado = datetime.datetime.now()
|
||||
# item.modificado = datetime.datetime.now()
|
||||
|
||||
db.session.add(item)
|
||||
db.session.commit()
|
||||
|
||||
if request.form['redirect']:
|
||||
return redirect(request.form['redirect'])
|
||||
else:
|
||||
return redirect(url_for('gestion.show', objetivoid=item.id))
|
||||
|
||||
|
||||
@gestion.route("/gestion/indicador", methods=['POST'])
|
||||
def indicador():
|
||||
# for key, values in request.form.items():
|
||||
# current_app.logger.debug('request: {}: {}'.format(key, values))
|
||||
|
||||
try:
|
||||
oid = int(request.form['oid'])
|
||||
pid = int(request.form['pid'])
|
||||
|
||||
except Exception as e:
|
||||
flash("Los datos ingresados son incorrectos: {}".format(str(e)), 'danger')
|
||||
abort(400)
|
||||
|
||||
try:
|
||||
ini = int(request.form['inicial'])
|
||||
val = int(request.form['valor'])
|
||||
tot = int(request.form['meta'])
|
||||
except:
|
||||
ini = 0
|
||||
val = 0
|
||||
tot = 100
|
||||
|
||||
if oid > 0:
|
||||
item = Avance.query.get(oid)
|
||||
item.nombre = request.form['nombre'].strip()[:100]
|
||||
|
||||
item.inicial = ini
|
||||
item.valor = val
|
||||
item.meta = tot
|
||||
db.session.commit()
|
||||
elif pid > 0:
|
||||
item = Avance(objetivoid=pid, nombre=request.form['nombre'].strip()[:100],
|
||||
inicial = ini, valor = val, meta = tot)
|
||||
db.session.add(item)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('gestion.show', objetivoid=item.objetivoid))
|
||||
|
||||
@gestion.route("/gestion/subelogo", methods=['POST'])
|
||||
def subelogo():
|
||||
# for key, values in request.form.items():
|
||||
# current_app.logger.debug('request: {}: {}'.format(key, values))
|
||||
try:
|
||||
cid = int(request.form['cid'])
|
||||
tipologo = int(request.form['tipologo'])
|
||||
|
||||
except Exception as e:
|
||||
flash(u"Los datos ingresados son incorrectos: {}".format(str(e)), 'danger')
|
||||
abort(400)
|
||||
|
||||
comision = Comision.query.filter(Comision.id==cid).one_or_none()
|
||||
|
||||
if comision is None:
|
||||
abort(404)
|
||||
|
||||
if comision.creado > 1:
|
||||
abort(403)
|
||||
|
||||
permiso = Miembro.query.filter(Miembro.comisionid==comision.id, Miembro.personaid==current_user.id).one_or_none()
|
||||
if permiso is None:
|
||||
abort(403)
|
||||
|
||||
if 'documento' in request.files and len(request.files['documento'].filename) > 0:
|
||||
from werkzeug.utils import secure_filename
|
||||
import os
|
||||
uploaded_file = request.files['documento']
|
||||
name = secure_filename(uploaded_file.filename)
|
||||
nombre, extension = os.path.splitext(name.lower())
|
||||
if extension not in ['.png']:
|
||||
flash(u"Solo se aceptan imágenes formato PNG", 'warning')
|
||||
else:
|
||||
from ilab_app.content.docs.utils import ingest_file
|
||||
(item, previtem) = ingest_file()
|
||||
|
||||
if tipologo == 1:
|
||||
flash(u"El logo fue subido con éxito", 'success')
|
||||
comision.logo = item.hash
|
||||
elif tipologo == 2:
|
||||
flash(u"El encabezado de página fue subido con éxito", 'success')
|
||||
comision.titulo = item.hash
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('gestion.manager', admin=comision.id))
|
||||
|
||||
|
||||
@gestion.route("/gestion/admin/<int:admin>")
|
||||
@gestion.route("/gestion/admin/")
|
||||
def manager(admin=0):
|
||||
|
||||
admindata = AccesoGestion()
|
||||
|
||||
current_app.logger.info('Admin Access: IP({}), USER({})'.format(g.ip.ipaddr, current_user.login))
|
||||
|
||||
if admin==0 or (admin not in admindata.admin):
|
||||
adming = next(admindata.administrador())
|
||||
return redirect(url_for('gestion.manager', admin=adming.id))
|
||||
else:
|
||||
return render_template('gestion/comisiones.html', title=u'Gestión', admindata=admindata, admin=admin, grupo='Responsabilidades', tag='gestion.manager')
|
||||
|
||||
@gestion.route("/gestion/modulos/")
|
||||
def modulos():
|
||||
|
||||
admindata = AccesoGestion()
|
||||
if not admindata.sysadmin:
|
||||
current_app.logger.warning('Modulo Access Denied: IP({}), USER({})'.format(g.ip.ipaddr, current_user.login))
|
||||
abort(403)
|
||||
|
||||
current_app.logger.info('Modulo Access: IP({}), USER({})'.format(g.ip.ipaddr, current_user.login))
|
||||
|
||||
return render_template('gestion/modulos.html', title=u'Módulos de Gestión', admindata=admindata, grupo='Responsabilidades', tag='gestion.modulos')
|
||||
|
||||
@gestion.route("/gestion/documentos/")
|
||||
def documentos():
|
||||
uri = "documentos"
|
||||
modulo = ModuloGestion.query.filter(ModuloGestion.uri==uri).one_or_none()
|
||||
|
||||
if modulo is None:
|
||||
current_app.logger.info('Acceso main {}: 404 IP({}), USER({})'.format(uri, g.ip.ipaddr, current_user.login))
|
||||
abort(404)
|
||||
|
||||
accessdata = AccesoGestion()
|
||||
|
||||
if modulo not in accessdata.usuario:
|
||||
current_app.logger.info('Acceso main {}: 403 IP({}), USER({})'.format(uri, g.ip.ipaddr, current_user.login))
|
||||
abort(403)
|
||||
|
||||
|
||||
return render_template('gestion/documentos.html', title=u'Almacen de {}'.format(modulo.nombre), root=modulo.nodobase, label=modulo.etiqueta, grupo='Responsabilidades', tag='gestion.{}'.format(modulo.uri), uri=uri)
|
||||
|
||||
|
||||
@gestion.route("/gestion/<uri>")
|
||||
def main(uri):
|
||||
modulo = ModuloGestion.query.filter(ModuloGestion.uri==uri).one_or_none()
|
||||
|
||||
if modulo is None:
|
||||
current_app.logger.info('Acceso main {}: 404 IP({}), USER({})'.format(uri, g.ip.ipaddr, current_user.login))
|
||||
abort(404)
|
||||
|
||||
accessdata = AccesoGestion()
|
||||
|
||||
if modulo not in accessdata.usuario:
|
||||
current_app.logger.info('Acceso main {}: 403 IP({}), USER({})'.format(uri, g.ip.ipaddr, current_user.login))
|
||||
abort(403)
|
||||
|
||||
|
||||
return render_template('gestion/resumenes.html', title=u'Tareas {}'.format(modulo.nombre), root=modulo.nodobase, label=modulo.etiqueta, grupo='Responsabilidades', tag='gestion.{}'.format(modulo.uri), uri=uri)
|
||||
|
||||
|
||||
@gestion.route("/gestion/ver/<int:objetivoid>")
|
||||
def show(objetivoid):
|
||||
root = Objetivo.query.filter(Objetivo.id==objetivoid).one_or_none()
|
||||
|
||||
if root is None:
|
||||
abort(404)
|
||||
if not root.visible:
|
||||
abort(403)
|
||||
|
||||
return render_template('gestion/objetivo.html', title=u'Tareas personales', subtitulo='Vista en Detalle', root=root, label=root.modulo.etiqueta, grupo='Responsabilidades', tag='gestion.{}'.format(root.modulo.uri))
|
||||
|
||||
@gestion.route("/gestion/pdf/listaacta/<int:actaid>")
|
||||
def listapdf(actaid):
|
||||
root = Acta.query.filter(Acta.id==actaid).one_or_none()
|
||||
|
||||
if root is None:
|
||||
abort(404)
|
||||
if not root.objetivo.visible:
|
||||
abort(403)
|
||||
|
||||
from .reporte import ListaActaPDF
|
||||
from tempfile import NamedTemporaryFile
|
||||
import traceback
|
||||
with NamedTemporaryFile() as tmp:
|
||||
try:
|
||||
nombre_reporte = ListaActaPDF(tmp.name, root)
|
||||
with NamedTemporaryFile() as tmp2:
|
||||
import subprocess
|
||||
cmd = subprocess.Popen([u"/usr/bin/ps2pdf {} {}".format(tmp.name, tmp2.name)], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
cmd.wait()
|
||||
return send_file(tmp2.name, as_attachment=True, download_name=nombre_reporte)
|
||||
except:
|
||||
current_app.logger.critical('Error al generar reporte: Objetivoid = {}'.format(actaid))
|
||||
current_app.logger.debug('Traceback {}'.format(traceback.format_exc()))
|
||||
abort(500)
|
||||
|
||||
@gestion.route("/gestion/pdf/acta/<int:actaid>")
|
||||
def actapdf(actaid):
|
||||
root = Acta.query.filter(Acta.id==actaid).one_or_none()
|
||||
|
||||
if root is None:
|
||||
abort(404)
|
||||
if not root.objetivo.visible:
|
||||
abort(403)
|
||||
|
||||
from .reporte import ActaPDF
|
||||
from tempfile import NamedTemporaryFile
|
||||
import traceback
|
||||
with NamedTemporaryFile() as tmp:
|
||||
try:
|
||||
nombre_reporte = ActaPDF(tmp.name, root)
|
||||
with NamedTemporaryFile() as tmp2:
|
||||
import subprocess
|
||||
cmd = subprocess.Popen([u"/usr/bin/ps2pdf {} {}".format(tmp.name, tmp2.name)], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
cmd.wait()
|
||||
return send_file(tmp2.name, as_attachment=True, download_name=nombre_reporte)
|
||||
except:
|
||||
current_app.logger.critical('Error al generar reporte: Objetivoid = {}'.format(actaid))
|
||||
current_app.logger.debug('Traceback {}'.format(traceback.format_exc()))
|
||||
abort(500)
|
||||
|
||||
|
||||
@gestion.route("/gestion/xls/reporte/<int:objetivoid>", methods=['GET'])
|
||||
def reportexls(objetivoid):
|
||||
|
||||
root = Objetivo.query.filter(Objetivo.id==objetivoid).one_or_none()
|
||||
|
||||
if root is None:
|
||||
abort(404)
|
||||
if not root.visible:
|
||||
abort(403)
|
||||
|
||||
|
||||
from openpyxl import Workbook
|
||||
from ilab_app.models.system import Persona
|
||||
from openpyxl.utils import get_column_letter
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
wb = Workbook()
|
||||
|
||||
ws = wb.active
|
||||
ws.title = root.nombre[:20]
|
||||
ws.cell(column=1, row=1, value="Fecha")
|
||||
ws.cell(column=2, row=1, value="Dedicación (Horas)")
|
||||
ws.cell(column=3, row=1, value="Responsable")
|
||||
ws.cell(column=4, row=1, value="Nombre")
|
||||
ws.cell(column=5, row=1, value="Descripcion")
|
||||
|
||||
i = 1
|
||||
|
||||
for Accion in root.iactividades:
|
||||
i = i + 1
|
||||
ws.cell(column=1, row=i, value=Accion.fecha)
|
||||
ws.cell(column=2, row=i, value=Accion.dedicacion)
|
||||
|
||||
if Accion.creadopor is not None:
|
||||
creador = Persona.query.filter(Persona.id==Accion.creadopor).one_or_none()
|
||||
ws.cell(column=3, row=i, value=creador.nombrecompleto)
|
||||
|
||||
ws.cell(column=4, row=i, value=Accion.nombre)
|
||||
ws.cell(column=5, row=i, value=Accion.descripcion)
|
||||
|
||||
with NamedTemporaryFile() as tmp:
|
||||
wb.save(tmp.name)
|
||||
return send_file(tmp.name, as_attachment=True, download_name="Reporte_{}.xls".format(root.nombre[:20]))
|
||||
|
||||
|
||||
@gestion.route("/gestion/pdf/reporte", methods=['POST'])
|
||||
@gestion.route("/gestion/pdf/reporte/<int:objetivoid>", methods=['GET'])
|
||||
def reporte(objetivoid=None):
|
||||
if objetivoid==None:
|
||||
try:
|
||||
objetivoid = int(request.form['oid'])
|
||||
except:
|
||||
objetivoid = None
|
||||
|
||||
root = Objetivo.query.filter(Objetivo.id==objetivoid).one_or_none()
|
||||
|
||||
if root is None:
|
||||
abort(404)
|
||||
if not root.visible:
|
||||
abort(403)
|
||||
|
||||
try:
|
||||
Fini = datetime.datetime.strptime(request.form['fini'], '%d/%m/%Y').date()
|
||||
Fend = datetime.datetime.strptime(request.form['fend'], '%d/%m/%Y').date()
|
||||
current_app.logger.info('Se especifica rango de {} hasta {} para el reporte'.format(Fini, Fend))
|
||||
except Exception as e:
|
||||
import traceback
|
||||
Fini = None
|
||||
Fend = None
|
||||
current_app.logger.info('No se especifica rango de fechas (valido) para el reporte')
|
||||
|
||||
from .reporte import ReportePDF
|
||||
from tempfile import NamedTemporaryFile
|
||||
import traceback
|
||||
with NamedTemporaryFile() as tmp:
|
||||
try:
|
||||
nombre_reporte = ReportePDF(tmp.name, root, Fini, Fend)
|
||||
# return send_file(tmp.name, as_attachment=True, download_name=nombre_reporte)
|
||||
with NamedTemporaryFile() as tmp2:
|
||||
import subprocess
|
||||
cmd = subprocess.Popen([u"/usr/bin/ps2pdf {} {}".format(tmp.name, tmp2.name)], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
cmd.wait()
|
||||
return send_file(tmp2.name, as_attachment=True, download_name=nombre_reporte)
|
||||
except:
|
||||
current_app.logger.critical('Error al generar reporte: Objetivoid = {}'.format(objetivoid))
|
||||
current_app.logger.debug('Traceback {}'.format(traceback.format_exc()))
|
||||
abort(500)
|
||||
|
||||
|
||||
|
||||
@gestion.route("/gestion/pdf/acta/<int:actaid>/<string:masivo>")
|
||||
@gestion.route("/gestion/pdf/acta/<int:actaid>/")
|
||||
def acta_copiame(actaid, masivo=None):
|
||||
root = Acta.query.filter(Acta.id==actaid).one_or_none()
|
||||
|
||||
if root is None:
|
||||
abort(404)
|
||||
if not root.objetivo.visible:
|
||||
abort(403)
|
||||
|
||||
from flask_mail import Message
|
||||
from ilab_app import mail
|
||||
from .reporte import ActaPDF
|
||||
from tempfile import NamedTemporaryFile
|
||||
import traceback
|
||||
|
||||
with NamedTemporaryFile() as tmp:
|
||||
try:
|
||||
nombre_reporte = ActaPDF(tmp.name, root)
|
||||
with NamedTemporaryFile() as tmp2:
|
||||
import subprocess
|
||||
cmd = subprocess.Popen([u"/usr/bin/ps2pdf {} {}".format(tmp.name, tmp2.name)], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
cmd.wait()
|
||||
|
||||
timestamp = datetime.datetime.today().strftime('%Y%m%d%H.%M%S')
|
||||
|
||||
if masivo is None:
|
||||
msg = Message('Copia de Acta {}, {}'.format(root.secuencia, root.objetivo.nombre), sender='noreply@ilab.cl', recipients=[current_user.correodefecto.correo])
|
||||
else:
|
||||
msg = Message('Copia de Acta {}, {}'.format(root.secuencia, root.objetivo.nombre), sender='noreply@ilab.cl', recipients=root.correosasistentes)
|
||||
|
||||
msg.body = '''Ésta acta corresponde a la reunión {},
|
||||
|
||||
Fecha : {} desde las {} hasta las {} horas
|
||||
Ubicación: {}
|
||||
|
||||
El detalle de los temas a tratar y los citados a asistir se encuentra en el documento adjunto.
|
||||
|
||||
Favor no contestar este mensaje, fue generado automáticamente por: {} a las {}'''.format(root.secuencia, root.horaini.strftime('%d/%m/%Y'), root.horaini.strftime('%H:%M'), root.horafin.strftime('%H:%M'), root.lugar, current_user.nombrecompleto, timestamp)
|
||||
with open(tmp.name, 'rb') as fp:
|
||||
msg.attach(filename=nombre_reporte, content_type="application/pdf", data=fp.read(), disposition=None, headers=None)
|
||||
|
||||
mail.send(msg)
|
||||
return redirect(url_for('gestion.show', objetivoid=root.objetivoid))
|
||||
except:
|
||||
current_app.logger.critical('Error al generar reporte: Objetivoid = {}'.format(actaid))
|
||||
current_app.logger.debug('Traceback {}'.format(traceback.format_exc()))
|
||||
abort(500)
|
|
@ -0,0 +1,21 @@
|
|||
# coding: utf-8
|
||||
from flask import render_template, flash, redirect, send_file, url_for, request, Blueprint, current_app, abort, g
|
||||
from flask_login import login_required, current_user
|
||||
from webinterface import db
|
||||
from sqlalchemy.sql import func
|
||||
from webinterface.models.gtfs_work import ArchivosGTFS
|
||||
import datetime
|
||||
|
||||
gtfs = Blueprint('gtfs', __name__)
|
||||
|
||||
@gtfs.before_request
|
||||
@login_required
|
||||
def verifica_permisos():
|
||||
pass
|
||||
|
||||
|
||||
@gtfs.route("/gtfs/resumen", methods=['GET'])
|
||||
def resumen():
|
||||
|
||||
listado_gtfs = ArchivosGTFS.query.all()
|
||||
return render_template('gtfs/resumen.html', listado_gtfs=listado_gtfs)
|
|
@ -0,0 +1,217 @@
|
|||
# coding: utf-8
|
||||
from flask import render_template, flash, redirect, url_for, request, Blueprint, session as httpsession, g, current_app
|
||||
from flask_login import login_user, current_user, logout_user, login_required
|
||||
from user_agents import parse
|
||||
from webinterface import db, bcrypt
|
||||
from webinterface.models.system import Ipaddr, Dispositivo, Sesion, Identidad, Ruta, Registro, Conexion, Correo, Sitio, Persona
|
||||
from webinterface.models.documentos import Documento
|
||||
from webinterface.models.gestion import Comision, Miembro, Objetivo
|
||||
from .utils import es_local
|
||||
import datetime
|
||||
|
||||
## Core provee la funcionalidad basica de autentificación usando las credenciales de iLab
|
||||
|
||||
main = Blueprint('main', __name__)
|
||||
|
||||
@main.route("/")
|
||||
@main.route("/home")
|
||||
def home():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('main.dashboard'))
|
||||
else:
|
||||
return redirect(url_for('main.login'))
|
||||
|
||||
@main.route("/dashboard")
|
||||
@login_required
|
||||
def dashboard():
|
||||
return render_template('dashboard.html', title='Dashboard')
|
||||
|
||||
@main.route("/about")
|
||||
def about():
|
||||
return render_template('about.html', title='About')
|
||||
|
||||
@main.route("/login", methods=['GET', 'POST'])
|
||||
def login():
|
||||
return redirect('https://tpmc.ilab.cl/system/login')
|
||||
|
||||
@main.route("/logout")
|
||||
def logout():
|
||||
return redirect('https://tpmc.ilab.cl/system/logout')
|
||||
|
||||
@main.route("/me")
|
||||
@login_required
|
||||
def me():
|
||||
image_file = url_for('static', filename='profile_pics/' + current_user.foto)
|
||||
return render_template('system/me.html', title=u'¿Quién soy?',
|
||||
image_file=image_file)
|
||||
|
||||
@main.before_app_first_request
|
||||
def init_database():
|
||||
try:
|
||||
db.drop_all()
|
||||
db.create_all()
|
||||
|
||||
id_internet = Identidad.query.filter(Identidad.login=='Internet').one_or_none()
|
||||
if id_internet is None:
|
||||
id_internet = Identidad(login='Internet')
|
||||
db.session.add(id_internet)
|
||||
|
||||
id_intranet = Identidad.query.filter(Identidad.login=='Intranet').one_or_none()
|
||||
if id_intranet is None:
|
||||
id_intranet = Identidad(login='Intranet')
|
||||
db.session.add(id_intranet)
|
||||
|
||||
id_buscador = Identidad.query.filter(Identidad.login=='Buscador').one_or_none()
|
||||
if id_buscador is None:
|
||||
id_buscador = Identidad(login='Buscador')
|
||||
db.session.add(id_buscador)
|
||||
|
||||
clave_persona = bcrypt.generate_password_hash('hola1234').decode('utf-8')
|
||||
ifiguero_persona = Persona.query.filter(login=='ifiguero').one_or_none()
|
||||
if ifiguero_persona is None:
|
||||
ifiguero_persona = Persona(login='ifiguero', clave=clave_persona, rut='13955977-0', nombres='Israel', apellidop='Figueroa')
|
||||
db.session.add(ifiguero_persona)
|
||||
else:
|
||||
ifiguero_persona.clave=clave_persona
|
||||
|
||||
clave_tpmc = bcrypt.generate_password_hash('tpmc').decode('utf-8')
|
||||
tpmc_persona = Persona.query.filter(login=='tpmc').one_or_none()
|
||||
if tpmc_persona is None:
|
||||
tpmc_persona = Persona(login='tpmc', clave=clave_tpmc, rut='tpmc', nombres='Transporte', apellidop='Público')
|
||||
db.session.add(tpmc_persona)
|
||||
else:
|
||||
tpmc_persona.clave=clave_tpmc
|
||||
|
||||
db.session.commit()
|
||||
|
||||
uuid_nill = "00000000000000000000000000000000"
|
||||
root_doc = Documento.query.filter(Documento.hash==uuid_nill).one_or_none()
|
||||
if root_doc is None:
|
||||
root_doc = Documento(hash=uuid_nill, nombre='Root', autorid=ifiguero_persona.id)
|
||||
db.session.add(root_doc)
|
||||
|
||||
root_comision = Comision.query.filter(id==1).one_or_none()
|
||||
if root_comision is None:
|
||||
root_comision = Comision(id=1, creado=1, nombre='root')
|
||||
db.session.add(root_comision)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
ifiguero_comision = Miembro.query.filter(Miembro.personaid==ifiguero_persona.id, Miembro.comisionid==1).one_or_none()
|
||||
if ifiguero_comision is None:
|
||||
ifiguero_comision = Miembro(personaid=ifiguero_persona.id, comisionid=1)
|
||||
db.session.add(ifiguero_comision)
|
||||
|
||||
root_object = Objetivo.query.filter(Objetivo.id==1).one_or_none()
|
||||
if root_object is None:
|
||||
root_object = Objetivo(id=1, parentid=1, responsableid=1, tipo='root', nombre='root')
|
||||
db.session.add(root_object)
|
||||
|
||||
|
||||
db.session.commit()
|
||||
|
||||
except:
|
||||
import traceback
|
||||
current_app.logger.debug(traceback.format_exc())
|
||||
|
||||
|
||||
|
||||
@main.before_app_request
|
||||
def registra_sesion():
|
||||
|
||||
if request.headers.getlist("X-Forwarded-For"):
|
||||
remote_ip = request.headers.getlist("X-Forwarded-For")[0]
|
||||
else:
|
||||
remote_ip = request.environ['REMOTE_ADDR']
|
||||
uadata = parse(request.user_agent.string)
|
||||
|
||||
ip = Ipaddr.query.filter_by(ipaddr=remote_ip).one_or_none()
|
||||
if ip is None:
|
||||
ip = Ipaddr(ipaddr=remote_ip)
|
||||
db.session.add(ip)
|
||||
db.session.commit()
|
||||
|
||||
if 'sid' in httpsession:
|
||||
sesion = Sesion.query.filter_by(id=httpsession['sid']).one_or_none()
|
||||
if sesion is None:
|
||||
httpsession.pop('sid', None)
|
||||
else:
|
||||
dispositivo = Dispositivo.query.filter_by(id=sesion.dispositivoid, parsed_ua=str(uadata)).one_or_none()
|
||||
if dispositivo is None:
|
||||
httpsession.pop('sid', None)
|
||||
else:
|
||||
conexion = Conexion.query.filter_by(ipaddrid=ip.id, sesionid=sesion.id).one_or_none()
|
||||
Ident = sesion.identidad
|
||||
if conexion is None:
|
||||
conexion = Conexion(ipaddr=ip, sesion=sesion)
|
||||
db.session.add(conexion)
|
||||
db.session.commit()
|
||||
|
||||
if 'sid' not in httpsession:
|
||||
|
||||
dispositivo = Dispositivo.query.filter_by(useragent=request.user_agent.string).one_or_none()
|
||||
if dispositivo is None:
|
||||
dev = uadata.is_pc and "PC" or uadata.device.family
|
||||
os = ("%s %s" % (uadata.os.family, uadata.os.version_string)).strip()
|
||||
browser = ("%s %s" % (uadata.browser.family, uadata.browser.version_string)).strip()
|
||||
|
||||
dispositivo = Dispositivo(useragent=request.user_agent.string, dev=dev , os=os , browser=browser, parsed_ua=str(uadata))
|
||||
db.session.add(dispositivo)
|
||||
db.session.commit()
|
||||
|
||||
if uadata.is_bot:
|
||||
Ident = Identidad.query.filter_by(login='Buscador').one()
|
||||
elif es_local(remote_ip):
|
||||
Ident = Identidad.query.filter_by(login='Intranet').one()
|
||||
else:
|
||||
Ident = Identidad.query.filter_by(login='Internet').one()
|
||||
|
||||
sesion = Sesion(identidad=Ident, dispositivo=dispositivo)
|
||||
db.session.add(sesion)
|
||||
db.session.commit()
|
||||
|
||||
conexion = Conexion(ipaddr=ip, sesion=sesion)
|
||||
db.session.add(conexion)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
# agregamos el registro asociado a la solicitud actual
|
||||
# a la sesion que esta actualmente cargada. Dejamos la sesion guardada en
|
||||
# la variable global g.
|
||||
|
||||
if '/reset_password' in str(request.path):
|
||||
rpth = '/reset_password'
|
||||
else:
|
||||
rpth = str(request.path)
|
||||
|
||||
solicitud = Ruta.query.filter_by(ruta=rpth).first()
|
||||
if solicitud is None:
|
||||
solicitud = Ruta(ruta=str(request.path))
|
||||
db.session.add(solicitud)
|
||||
db.session.commit()
|
||||
|
||||
host = Sitio.query.filter_by(sitio=request.host).first()
|
||||
if host is None:
|
||||
host = Sitio(sitio=request.host)
|
||||
db.session.add(host)
|
||||
db.session.commit()
|
||||
|
||||
tamano = int(request.headers.get('Content-Length') or 0)
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
registro = Registro(sitio=host, ruta=solicitud, sesion=sesion, ipaddr=ip, tamano=tamano)
|
||||
db.session.add(registro)
|
||||
db.session.commit()
|
||||
|
||||
conexion.ultimo = now
|
||||
sesion.ultimo = now
|
||||
db.session.commit()
|
||||
|
||||
g.ip = ip
|
||||
g.user = Ident
|
||||
g.sesion = sesion
|
||||
g.conexion = conexion
|
||||
g.registro = registro
|
||||
g.dispositivo = dispositivo
|
||||
httpsession['sid'] = g.sesion.id
|
|
@ -0,0 +1,8 @@
|
|||
# coding: utf-8
|
||||
import os
|
||||
from itertools import cycle
|
||||
|
||||
def es_local(ip='127.0.0.1'):
|
||||
from netaddr import IPAddress
|
||||
return IPAddress(ip).is_private()
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.sql import func
|
||||
from .system import Persona
|
||||
from webinterface import db
|
||||
|
||||
class Documento(db.Model):
|
||||
|
||||
__tablename__ = 'archivos'
|
||||
__table_args__ = { 'schema': 'doc' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
hash = db.Column(UUID(as_uuid=True), primary_key=True, nullable=False, autoincrement=False)
|
||||
|
||||
nombre = db.Column(db.String, nullable=False)
|
||||
descripcion = db.Column(db.Text)
|
||||
tamano = db.Column(db.Integer, default=0)
|
||||
|
||||
reemplazahash = db.Column(UUID(as_uuid=True), db.ForeignKey('doc.archivos.hash'), nullable=True)
|
||||
|
||||
creado = db.Column(db.DateTime, server_default=func.now())
|
||||
autorid = db.Column(db.BigInteger, db.ForeignKey('usuarios.personas.id'))
|
||||
|
||||
reemplaza = db.relationship('Documento')
|
||||
# evidenciade = db.relationship('Evidencia')
|
||||
autor = db.relationship('Persona')
|
||||
|
||||
|
||||
@property
|
||||
def humansize(self):
|
||||
if self.tamano == 0:
|
||||
return "link"
|
||||
|
||||
if self.tamano < 1000:
|
||||
return "{} B".format(self.tamano)
|
||||
elif self.tamano < 1000000:
|
||||
return "{:.2f} KB".format(self.tamano/1024)
|
||||
elif self.tamano < 1000000000:
|
||||
return "{:.2f} MB".format(self.tamano/1048576)
|
||||
else:
|
||||
return "{:.2fs} GB".format(self.tamano/1073741824)
|
||||
|
||||
def get_file(self):
|
||||
if self.tamano == 0:
|
||||
return None
|
||||
else:
|
||||
hashstr = str(self.hash)
|
||||
return 'ilab_app/archivos/{}/{}-{}'.format(hashstr[:2],hashstr, self.nombre[:150])
|
||||
|
||||
def get_dir(self):
|
||||
if self.tamano == 0:
|
||||
return None
|
||||
else:
|
||||
hashstr = str(self.hash)
|
||||
return 'ilab_app/archivos/{}'.format(hashstr[:2])
|
||||
|
||||
@property
|
||||
def placeholder(self):
|
||||
if self.tamano > 0:
|
||||
return False
|
||||
elif self.nombre[:7] == 'http://' or self.nombre[:8] == 'https://':
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
# @property
|
||||
# def objetivos(self):
|
||||
# for evidencia in self.evidenciade:
|
||||
# yield evidencia.actividad.objetivo
|
||||
#
|
||||
# @property
|
||||
# def visible(self):
|
||||
# from flask import current_app
|
||||
# from ilab_app.models.system import pertenece
|
||||
# comisiones = []
|
||||
# for objetivo in self.objetivos:
|
||||
# comisiones = comisiones + [objetivo.responsableid, objetivo.invitadosid]
|
||||
# return pertenece(0, comisiones)
|
|
@ -0,0 +1,622 @@
|
|||
# coding: utf-8
|
||||
#from datetime import datetime
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.dialects import sqlite
|
||||
|
||||
from webinterface import db
|
||||
from flask_login import current_user
|
||||
from flask import current_app, g
|
||||
import datetime
|
||||
import unidecode
|
||||
from webinterface.models.system import Persona
|
||||
from webinterface.models.documentos import Documento
|
||||
|
||||
|
||||
class Comision(db.Model):
|
||||
__tablename__ = 'comisiones'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
nombre = db.Column(db.String(100), nullable=False)
|
||||
descripcion = db.Column(db.String(100))
|
||||
creado = db.Column(db.Integer, db.ForeignKey('gestion.comisiones.id'), nullable=False)
|
||||
logo = db.Column(UUID(as_uuid=True), nullable=True, autoincrement=False)
|
||||
titulo = db.Column(UUID(as_uuid=True), nullable=True, autoincrement=False)
|
||||
|
||||
__table_args__ = (db.UniqueConstraint('nombre', 'creado', name='nodupesComisiones'), { 'schema': 'gestion' })
|
||||
|
||||
@property
|
||||
def supervisor(self):
|
||||
return Comision.query.filter(Comision.id==self.creado).one_or_none()
|
||||
|
||||
@property
|
||||
def quorum(self):
|
||||
miembro = Miembro.query.filter(Miembro.comisionid==self.id).count()
|
||||
if miembro == 0:
|
||||
return False
|
||||
else:
|
||||
return miembro
|
||||
|
||||
@property
|
||||
def miembros(self):
|
||||
for miembro in Miembro.query.filter(Miembro.comisionid==self.id).all():
|
||||
yield miembro.persona
|
||||
|
||||
def mvector(self):
|
||||
vector = []
|
||||
for persona in self.miembros:
|
||||
vector.append(persona.id)
|
||||
|
||||
return vector
|
||||
|
||||
|
||||
class Miembro(db.Model):
|
||||
__tablename__ = 'miembros'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
personaid = db.Column(db.BigInteger, db.ForeignKey('usuarios.personas.id'))
|
||||
comisionid = db.Column(db.Integer, db.ForeignKey('gestion.comisiones.id'))
|
||||
|
||||
persona = db.relationship('Persona')
|
||||
comision = db.relationship('Comision')
|
||||
|
||||
class Objetivo(db.Model):
|
||||
__tablename__ = 'objetivos'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
tipo = db.Column(db.String(100), nullable=False, default=u'Estratégico')
|
||||
nombre = db.Column(db.String(200), nullable=False)
|
||||
descripcion = db.Column(db.Text)
|
||||
|
||||
parentid = db.Column(db.Integer, db.ForeignKey('gestion.objetivos.id'), nullable=False)
|
||||
responsableid = db.Column(db.Integer, db.ForeignKey('gestion.comisiones.id'), nullable=False)
|
||||
invitadosid = db.Column(db.Integer, db.ForeignKey('gestion.comisiones.id'), nullable=True)
|
||||
|
||||
# indicadores = db.relationship('Avance', back_populates="objetivo", order_by='Accion.id.desc()')
|
||||
actas = db.relationship('Acta', back_populates="objetivo", order_by='Acta.secuencia.desc()')
|
||||
actividades = db.relationship('Accion', back_populates="objetivo", order_by='Accion.fecha.desc()')
|
||||
responsable = db.relationship('Comision', foreign_keys=[responsableid])
|
||||
invitados = db.relationship('Comision', foreign_keys=[invitadosid])
|
||||
|
||||
estado = db.Column(db.SmallInteger, default=100)
|
||||
|
||||
## registros de auditoria
|
||||
creado = db.Column(db.DateTime, default=func.now())
|
||||
creadopor = db.Column(db.Integer, db.ForeignKey('usuarios.personas.id'), nullable=True)
|
||||
modificado = db.Column(db.DateTime, default=func.now(), onupdate=func.now())
|
||||
modificadopor = db.Column(db.Integer, db.ForeignKey('usuarios.personas.id'), nullable=True)
|
||||
|
||||
@property
|
||||
def actasr(self):
|
||||
for item in Acta.query.filter(Acta.objetivoid==self.id).order_by(Acta.secuencia.asc()).all():
|
||||
yield item
|
||||
|
||||
@property
|
||||
def sub_HDS(self):
|
||||
return round(self.sub_horas/26,2)
|
||||
|
||||
@property
|
||||
def modulo(self):
|
||||
return ModuloGestion.query.filter(ModuloGestion.basenodeid==self.get_root().parentid).one_or_none()
|
||||
|
||||
@property
|
||||
def modulo_raw(self):
|
||||
return ModuloGestion.query.filter(ModuloGestion.basenodeid==self.id).one_or_none()
|
||||
|
||||
@property
|
||||
def indicadores(self):
|
||||
for item in Avance.query.filter(Avance.objetivoid==self.id).order_by(Avance.id.asc()).all():
|
||||
yield item
|
||||
|
||||
@property
|
||||
def iactividades(self):
|
||||
for item in Accion.query.filter(Accion.objetivoid==self.id).order_by(Accion.fecha.asc()).all():
|
||||
yield item
|
||||
|
||||
@property
|
||||
def hijos(self):
|
||||
for item in Objetivo.query.filter(Objetivo.parentid==self.id, Objetivo.estado<100).order_by(Objetivo.id.asc()).all():
|
||||
yield item
|
||||
for item in Objetivo.query.filter(Objetivo.parentid==self.id, Objetivo.estado>=100).order_by(Objetivo.id.asc()).all():
|
||||
yield item
|
||||
|
||||
@property
|
||||
def padre(self):
|
||||
return Objetivo.query.get(self.parentid)
|
||||
|
||||
@property
|
||||
def icon_class(self):
|
||||
return self.get_root().tipo
|
||||
|
||||
@property
|
||||
def sub_actividades(self):
|
||||
i = 0
|
||||
for item in self.hijos:
|
||||
i += item.sub_actividades
|
||||
for item in self.actividades:
|
||||
i += 1
|
||||
return i
|
||||
|
||||
@property
|
||||
def sub_horas(self):
|
||||
h = 0
|
||||
for item in self.hijos:
|
||||
h += item.sub_horas
|
||||
for item in self.actividades:
|
||||
h += item.dedicacion
|
||||
for item in self.actas:
|
||||
h += item.dedicacion
|
||||
|
||||
return h
|
||||
@property
|
||||
def sub_HDS(self):
|
||||
return round(self.sub_horas/26,2)
|
||||
|
||||
@property
|
||||
def sub_avanceNormalizado(self):
|
||||
base = 0
|
||||
numBase = 0
|
||||
|
||||
for item in self.hijos:
|
||||
base += item.sub_avanceNormalizado
|
||||
numBase += 1
|
||||
|
||||
for item in self.indicadores:
|
||||
base += item.avanceNormalizado
|
||||
numBase += 1
|
||||
|
||||
return base/numBase if numBase else 0
|
||||
|
||||
@property
|
||||
def sub_avance(self):
|
||||
return round(100*self.sub_avanceNormalizado)
|
||||
|
||||
def is_root(self):
|
||||
if self.padre.parentid == 1:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_root(self):
|
||||
|
||||
node = self
|
||||
while node.padre.parentid > 1:
|
||||
node = node.padre
|
||||
|
||||
return node
|
||||
|
||||
@property
|
||||
def comisiones_validas(self):
|
||||
node = self.get_root()
|
||||
|
||||
yield node.responsable
|
||||
|
||||
for comision in Comision.query.filter(Comision.creado == node.responsableid).all():
|
||||
yield comision
|
||||
|
||||
@property
|
||||
def miembros_responsables(self):
|
||||
for miembro in self.responsable.miembros:
|
||||
yield miembro
|
||||
|
||||
@property
|
||||
def miembros_habilitados(self):
|
||||
vistos = []
|
||||
for miembro in self.responsable.miembros:
|
||||
if miembro not in vistos:
|
||||
vistos.append(miembro)
|
||||
yield miembro
|
||||
if self.invitados is not None:
|
||||
for miembro in self.invitados.miembros:
|
||||
if miembro not in vistos:
|
||||
vistos.append(miembro)
|
||||
yield miembro
|
||||
|
||||
@property
|
||||
def archivado(self):
|
||||
if self.estado < 100:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def lineage(self):
|
||||
ancestrys=[]
|
||||
|
||||
node = self.padre
|
||||
while node.parentid > 1:
|
||||
ancestrys.append(node)
|
||||
node = node.padre
|
||||
|
||||
for node in reversed(ancestrys):
|
||||
yield node
|
||||
|
||||
def get_comisiones(self, lvl=1, archived=False):
|
||||
|
||||
if self.invitadosid is None:
|
||||
comisiones = [self.responsableid]
|
||||
else:
|
||||
comisiones = [self.responsableid, self.invitadosid]
|
||||
if lvl > 0:
|
||||
lvl = lvl - 1
|
||||
for objetivo in self.hijos:
|
||||
if objetivo.estado is None or objetivo.estado<100 or archived:
|
||||
comisiones = comisiones + objetivo.get_comisiones(lvl=lvl, archived=archived)
|
||||
|
||||
return comisiones
|
||||
|
||||
def visible(self, lvl=5, archived=False):
|
||||
comisiones = self.get_comisiones(lvl=lvl, archived=archived)
|
||||
if len(comisiones)>0:
|
||||
comisiones = list(set(comisiones))
|
||||
try:
|
||||
comisiones.remove(1)
|
||||
except:
|
||||
pass
|
||||
existe = Miembro.query.filter(Miembro.comisionid.in_(comisiones), Miembro.personaid==current_user.id).first()
|
||||
if existe is None:
|
||||
# current_app.logger.debug('[No-Visble] Nodo {}: {} ({})'.format(self.nombre, current_user.login, comisiones))
|
||||
return False
|
||||
else:
|
||||
# current_app.logger.debug('[Visible] Nodo {}: {} ({})'.format(self.nombre, current_user.login, comisiones))
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_manager(self):
|
||||
|
||||
|
||||
comisiones = list(set( [ self.responsableid, self.padre.responsableid ] ))
|
||||
|
||||
existe = Miembro.query.filter(Miembro.comisionid.in_(comisiones), Miembro.personaid==current_user.id).first()
|
||||
if existe is None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
class Avance(db.Model):
|
||||
__tablename__ = 'indicadores'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
objetivoid = db.Column(db.Integer, db.ForeignKey('gestion.objetivos.id'))
|
||||
|
||||
nombre = db.Column(db.String(200), nullable=False)
|
||||
|
||||
inicial = db.Column(db.Integer, default=0)
|
||||
valor = db.Column(db.Integer)
|
||||
meta = db.Column(db.Integer, default=100)
|
||||
ponderacion = db.Column(db.Integer, default=1)
|
||||
|
||||
objetivo = db.relationship('Objetivo')
|
||||
|
||||
@property
|
||||
def progreso(self):
|
||||
if self.valor>self.inicial:
|
||||
return self.valor-self.inicial
|
||||
else:
|
||||
return 0
|
||||
@property
|
||||
def previo(self):
|
||||
return int(100.0*self.inicial/self.meta)
|
||||
|
||||
@property
|
||||
def avanceNormalizado(self):
|
||||
return min(self.progreso/(self.meta-self.inicial), 1)
|
||||
|
||||
@property
|
||||
def avance(self):
|
||||
return int(100.0*self.progreso/self.meta)
|
||||
|
||||
class Acta(db.Model):
|
||||
__tablename__ = 'actas'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
objetivoid = db.Column(db.Integer, db.ForeignKey('gestion.objetivos.id'))
|
||||
secuencia = db.Column(db.Integer)
|
||||
|
||||
horaini = db.Column(db.DateTime)
|
||||
horafin = db.Column(db.DateTime)
|
||||
|
||||
lugar = db.Column(db.String, nullable=False)
|
||||
|
||||
temas = db.Column(db.Text)
|
||||
desarrollo = db.Column(db.Text)
|
||||
acuerdos = db.Column(db.Text)
|
||||
|
||||
objetivo = db.relationship('Objetivo', back_populates='actas')
|
||||
miembros = db.relationship('MiembrosAsistentes', back_populates='acta')
|
||||
invitados = db.relationship('InvitadosAsistentes', back_populates='acta')
|
||||
anexos = db.relationship('AnexosActa', back_populates='acta')
|
||||
|
||||
## registros de auditoria
|
||||
creado = db.Column(db.DateTime, default=func.now())
|
||||
creadopor = db.Column(db.Integer, db.ForeignKey('usuarios.personas.id'), nullable=True)
|
||||
modificado = db.Column(db.DateTime, default=func.now(), onupdate=func.now())
|
||||
modificadopor = db.Column(db.Integer, db.ForeignKey('usuarios.personas.id'), nullable=True)
|
||||
|
||||
@property
|
||||
def documentos(self):
|
||||
for evidencia in self.anexos:
|
||||
yield evidencia.documento
|
||||
|
||||
@property
|
||||
def dedicacion(self):
|
||||
import datetime
|
||||
now = datetime.datetime.now()
|
||||
if self.horaini < now and self.horafin < now:
|
||||
timedelta = self.horafin - self.horaini
|
||||
secondsdelta = timedelta.total_seconds()
|
||||
horasdelta = secondsdelta / 3600.0
|
||||
else:
|
||||
horasdelta = 0
|
||||
|
||||
return horasdelta
|
||||
|
||||
@property
|
||||
def correosasistentes(self):
|
||||
asistentes = []
|
||||
for persona in self.miembros:
|
||||
asistentes.append( (persona.persona.nombrecompleto, persona.persona.correodefecto.correo) )
|
||||
for persona in self.invitados:
|
||||
asistentes.append( (persona.nombre, persona.correo) )
|
||||
return asistentes
|
||||
|
||||
@property
|
||||
def asistentes(self):
|
||||
asistentes = []
|
||||
for persona in self.miembros:
|
||||
asistentes.append( persona.persona.nombrecompleto )
|
||||
for persona in self.invitados:
|
||||
asistentes.append( persona.nombre )
|
||||
return asistentes
|
||||
|
||||
@property
|
||||
def txtasistentes(self):
|
||||
asistentes = []
|
||||
for persona in self.miembros:
|
||||
asistentes.append( persona.persona.nombrecompleto )
|
||||
for persona in self.invitados:
|
||||
asistentes.append( u"{} ({})".format(persona.nombre, persona.correo) )
|
||||
|
||||
return ", ".join(asistentes)
|
||||
|
||||
@property
|
||||
def miembrosid(self):
|
||||
listado = []
|
||||
for persona in self.miembros:
|
||||
listado.append( persona.personaid )
|
||||
return listado
|
||||
|
||||
@property
|
||||
def invitadostextarea(self):
|
||||
listado = []
|
||||
for persona in self.invitados:
|
||||
listado.append( u"{} {}".format(persona.correo, persona.nombre))
|
||||
return "\n".join(listado)
|
||||
|
||||
@property
|
||||
def documentos(self):
|
||||
for anexo in self.anexos:
|
||||
yield anexo.documento
|
||||
|
||||
|
||||
class AnexosActa(db.Model):
|
||||
__tablename__ = 'acta_anexos'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
actaid = db.Column(db.Integer, db.ForeignKey('gestion.actas.id'))
|
||||
documentohash = db.Column(UUID(as_uuid=True), db.ForeignKey('doc.archivos.hash'))
|
||||
|
||||
__table_args__ = (db.UniqueConstraint('actaid', 'documentohash', name='nodupesAnexosActa'), { 'schema': 'gestion' })
|
||||
|
||||
acta = db.relationship('Acta', back_populates='anexos')
|
||||
documento = db.relationship('Documento')
|
||||
|
||||
|
||||
class MiembrosAsistentes(db.Model):
|
||||
__tablename__ = 'acta_asistentes'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
personaid = db.Column(db.BigInteger, db.ForeignKey('usuarios.personas.id'))
|
||||
actaid = db.Column(db.Integer, db.ForeignKey('gestion.actas.id'))
|
||||
|
||||
acta = db.relationship('Acta', back_populates='miembros')
|
||||
persona = db.relationship('Persona')
|
||||
__table_args__ = (db.UniqueConstraint('personaid', 'actaid', name='nodupesMiembrosActas'), { 'schema': 'gestion' })
|
||||
|
||||
|
||||
class InvitadosAsistentes(db.Model):
|
||||
__tablename__ = 'acta_invitados'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
correo = db.Column(db.String, nullable=False)
|
||||
nombre = db.Column(db.String, nullable=False)
|
||||
actaid = db.Column(db.Integer, db.ForeignKey('gestion.actas.id'))
|
||||
|
||||
acta = db.relationship('Acta', back_populates='invitados')
|
||||
__table_args__ = (db.UniqueConstraint('correo', 'actaid', name='nodupesInvitadosActas'), { 'schema': 'gestion' })
|
||||
|
||||
|
||||
class Accion(db.Model):
|
||||
__tablename__ = 'actividades'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
objetivoid = db.Column(db.Integer, db.ForeignKey('gestion.objetivos.id'))
|
||||
|
||||
nombre = db.Column(db.String(200), nullable=False)
|
||||
descripcion = db.Column(db.Text)
|
||||
|
||||
fecha = db.Column(db.Date)
|
||||
estado = db.Column(db.SmallInteger, default=100)
|
||||
dedicacion = db.Column(db.Integer, default=0)
|
||||
|
||||
objetivo = db.relationship('Objetivo', back_populates='actividades')
|
||||
evidencias = db.relationship('Evidencia', back_populates='actividad')
|
||||
|
||||
## registros de auditoria
|
||||
creado = db.Column(db.DateTime, default=func.now())
|
||||
creadopor = db.Column(db.Integer, db.ForeignKey('usuarios.personas.id'), nullable=True)
|
||||
modificado = db.Column(db.DateTime, default=func.now(), onupdate=func.now())
|
||||
modificadopor = db.Column(db.Integer, db.ForeignKey('usuarios.personas.id'), nullable=True)
|
||||
|
||||
|
||||
@property
|
||||
def documentos(self):
|
||||
for evidencia in self.evidencias:
|
||||
yield evidencia.documento
|
||||
|
||||
class Evidencia(db.Model):
|
||||
__tablename__ = 'evidencias'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
actividadid = db.Column(db.Integer, db.ForeignKey('gestion.actividades.id'))
|
||||
documentohash = db.Column(UUID(as_uuid=True), db.ForeignKey('doc.archivos.hash'))
|
||||
|
||||
__table_args__ = (db.UniqueConstraint('actividadid', 'documentohash', name='nodupesEvidencia'), { 'schema': 'gestion' })
|
||||
|
||||
actividad = db.relationship('Accion', back_populates='evidencias')
|
||||
documento = db.relationship('Documento')
|
||||
|
||||
class ModuloGestion(db.Model):
|
||||
__tablename__ = 'modulo'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
basenodeid = db.Column(db.Integer, db.ForeignKey('gestion.objetivos.id'))
|
||||
nombre = db.Column(db.String(100), nullable=False)
|
||||
uri = db.Column(db.String(100), nullable=False, unique=True)
|
||||
etiqueta = db.Column(db.String(100), nullable=False)
|
||||
|
||||
nodobase = db.relationship('Objetivo')
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
return self.nodobase.tipo
|
||||
|
||||
@property
|
||||
def quorum(self):
|
||||
return AccesosModuloGestion.query.filter(AccesosModuloGestion.moduloid==self.id).count()
|
||||
|
||||
def mvector(self):
|
||||
vector = []
|
||||
for acceso in AccesosModuloGestion.query.filter(AccesosModuloGestion.moduloid==self.id).all():
|
||||
vector.append(acceso.comisionid)
|
||||
|
||||
return vector
|
||||
|
||||
class AccesosModuloGestion(db.Model):
|
||||
__tablename__ = 'acceso'
|
||||
__table_args__ = { 'schema': 'gestion' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
moduloid = db.Column(db.Integer, db.ForeignKey('gestion.modulo.id'))
|
||||
comisionid = db.Column(db.Integer, db.ForeignKey('gestion.comisiones.id'))
|
||||
|
||||
__table_args__ = (db.UniqueConstraint('moduloid', 'comisionid', name='no_dupes_acceso_gestion'), { 'schema': 'gestion' })
|
||||
|
||||
modulo = db.relationship('ModuloGestion')
|
||||
comision = db.relationship('Comision')
|
||||
|
||||
|
||||
class AccesoGestion():
|
||||
def __init__(self):
|
||||
if 'accesogestion' not in g:
|
||||
self.sysadmin = False
|
||||
self.admin = []
|
||||
self.usuario = []
|
||||
for modulo in self.get_modulos():
|
||||
if modulo.nodobase.visible():
|
||||
self.usuario.append(modulo)
|
||||
|
||||
for comision in Comision.query.join(Miembro).filter(Miembro.personaid==current_user.id, Comision.creado==1).all():
|
||||
if comision.id > 999:
|
||||
self.admin.append(comision.id)
|
||||
elif comision.id == 1:
|
||||
self.sysadmin = True
|
||||
self.admin.append(comision.id)
|
||||
|
||||
g.accesogestion = {
|
||||
'sysadmin': self.sysadmin,
|
||||
'admin': self.admin,
|
||||
'usuario': self.usuario
|
||||
}
|
||||
else:
|
||||
self.sysadmin = g.accesogestion['sysadmin']
|
||||
self.admin = g.accesogestion['admin']
|
||||
self.usuario = g.accesogestion['usuario']
|
||||
|
||||
def get_nodes(self):
|
||||
for modulo in self.usuario:
|
||||
yield modulo
|
||||
|
||||
def get_modulos(self):
|
||||
for modulo in ModuloGestion.query.order_by(ModuloGestion.id.asc()).all():
|
||||
yield modulo
|
||||
|
||||
def in_gestion(self):
|
||||
if len(self.usuario) > 0:
|
||||
return True
|
||||
else:
|
||||
return self.sysadmin
|
||||
|
||||
def is_sysadmin(self):
|
||||
return self.sysadmin
|
||||
|
||||
def is_admin(self):
|
||||
if len(self.admin)>0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def administrador(self):
|
||||
for item in self.admin:
|
||||
yield Comision.query.get(item)
|
||||
|
||||
def comisiones(self, admin):
|
||||
if admin > 1:
|
||||
yield Comision.query.get(admin)
|
||||
for item in Comision.query.filter(Comision.creado.in_(self.admin), Comision.creado==admin).all():
|
||||
yield item
|
||||
elif self.sysadmin:
|
||||
for item in Comision.query.filter(Comision.creado==1).order_by(Comision.id.asc()).all():
|
||||
yield item
|
||||
|
||||
def valid_users(self, admin):
|
||||
if admin > 1:
|
||||
for item in Miembro.query.join(Comision).filter(Comision.creado==1).distinct(Miembro.personaid).all():
|
||||
yield Persona.query.get(item.personaid)
|
||||
elif self.sysadmin:
|
||||
for item in Persona.query.order_by(Persona.nombres.asc(), Persona.apellidop.asc()).all():
|
||||
yield item
|
|
@ -0,0 +1,281 @@
|
|||
#from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
|
||||
#from datetime import datetime
|
||||
#from flask import current_app
|
||||
from flask_login import UserMixin
|
||||
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.dialects import postgresql, sqlite
|
||||
from webinterface import db
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return Persona.query.filter(Persona.rut==user_id).one_or_none()
|
||||
|
||||
class Persona(Cuenta,UserMixin):
|
||||
__tablename__ = 'personas'
|
||||
__table_args__ = { 'schema': 'usuarios' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), db.ForeignKey('usuarios.cuentas.id'), primary_key=True)
|
||||
|
||||
nombres = db.Column(db.String(100), nullable=False)
|
||||
apellidop = db.Column(db.String(100), nullable=False)
|
||||
apellidom = db.Column(db.String(100))
|
||||
|
||||
rut = db.Column(db.String(20), unique=True, nullable=False)
|
||||
telefono = db.Column(db.String(20), nullable=True)
|
||||
foto = db.Column(db.String(50), nullable=False, default='default.jpg')
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity':'Persona'
|
||||
}
|
||||
|
||||
def get_id(self):
|
||||
return self.rut
|
||||
|
||||
@property
|
||||
def nombrecompleto(self):
|
||||
if self.apellidom is not None:
|
||||
return u"{} {} {}.".format(self.nombres.partition(' ')[0], self.apellidop, self.apellidom[0])
|
||||
else:
|
||||
return u"{} {}.".format(self.nombres.partition(' ')[0], self.apellidop)
|
||||
|
||||
@property
|
||||
def safe_nombrecompleto(self):
|
||||
return "{}".format(self.nombrecompleto.encode('utf-8'))
|
||||
@property
|
||||
def ascii_nombrecompleto(self):
|
||||
return unidecode.unidecode(self.nombrecompleto)
|
||||
|
||||
def is_in(self, comision=0):
|
||||
return True
|
||||
|
||||
@property
|
||||
def sidebar_menu(self):
|
||||
import json
|
||||
from .gestion import AccesoGestion
|
||||
# Primer menu, de carrera Visión de jefe de carrera
|
||||
amGestion = AccesoGestion()
|
||||
|
||||
if amGestion.in_gestion():
|
||||
cat = u"Gestión"
|
||||
links = []
|
||||
|
||||
for modulo in amGestion.get_nodes():
|
||||
links.append((modulo.nodobase.nombre, modulo.nodobase.tipo, modulo.nodobase.descripcion, url_for('gestion.main', uri=modulo.uri) ))
|
||||
|
||||
|
||||
class Identidad(db.Model):
|
||||
__tablename__ = 'identidades'
|
||||
__table_args__ = { 'schema': 'usuarios' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), primary_key=True, autoincrement=True)
|
||||
|
||||
login = db.Column(db.String(50), unique=True, nullable=False)
|
||||
alias = db.Column(db.String(50))
|
||||
|
||||
creado = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||
modificado = db.Column(db.DateTime(timezone=True), server_onupdate=func.now())
|
||||
|
||||
sesiones = db.relationship('Sesion', back_populates='identidad', lazy=True)
|
||||
ubicaciones = db.relationship('Ubicacion', back_populates='identidad', lazy=True)
|
||||
|
||||
tipo = db.Column(db.String(20))
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity':'Identidad',
|
||||
'polymorphic_on':tipo
|
||||
|
||||
}
|
||||
|
||||
class Cuenta(Identidad):
|
||||
__tablename__ = 'cuentas'
|
||||
__table_args__ = { 'schema': 'usuarios' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), db.ForeignKey('usuarios.identidades.id'), primary_key=True)
|
||||
|
||||
clave = db.Column(db.String(60), nullable=False)
|
||||
sysadmin = db.Column(db.Boolean, default=0)
|
||||
ultimoacceso = db.Column(db.DateTime(timezone=True))
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity':'Cuenta'
|
||||
}
|
||||
|
||||
correos = db.relationship('Correo', back_populates='cuenta', lazy=True)
|
||||
tarjetas = db.relationship('Tarjeta', back_populates='cuenta', lazy=True)
|
||||
# credenciales = db.relationship('Credencial', back_populates='cuenta', lazy=True)
|
||||
|
||||
def set_default(self, correo):
|
||||
for item in self.correos:
|
||||
if correo == item:
|
||||
correo.default = True
|
||||
else:
|
||||
correo.default = False
|
||||
|
||||
@property
|
||||
def correodefecto(self):
|
||||
last = None
|
||||
for item in self.correos:
|
||||
if item.default == True:
|
||||
return item
|
||||
elif last is None:
|
||||
last = item
|
||||
|
||||
return last
|
||||
|
||||
@correodefecto.setter
|
||||
def correodefecto(self, correo):
|
||||
if correo.cuenta != self:
|
||||
pass
|
||||
|
||||
for item in self.correos:
|
||||
if item == correo:
|
||||
item.default = True
|
||||
else:
|
||||
item.default = False
|
||||
|
||||
class Correo(db.Model):
|
||||
__tablename__ = 'correos'
|
||||
__table_args__ = { 'schema': 'usuarios' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
correo = db.Column(db.String(100), unique=True, nullable=False)
|
||||
|
||||
cuentaid = db.Column(db.BigInteger(), db.ForeignKey('usuarios.cuentas.id'), nullable=False)
|
||||
cuenta = db.relationship('Cuenta', back_populates='correos')
|
||||
default = db.Column(db.Boolean, default=0)
|
||||
|
||||
|
||||
class Sesion(db.Model):
|
||||
__tablename__ = 'sesiones'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), primary_key=True)
|
||||
|
||||
identidadid = db.Column(db.BigInteger(), db.ForeignKey('usuarios.identidades.id'), nullable=False)
|
||||
identidad = db.relationship('Identidad', back_populates='sesiones')
|
||||
|
||||
dispositivoid = db.Column(db.Integer, db.ForeignKey('registros.dispositivos.id'), nullable=False)
|
||||
dispositivo = db.relationship('Dispositivo')
|
||||
|
||||
conexiones = db.relationship('Conexion')
|
||||
|
||||
iniciada = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||
ultimo = db.Column(db.DateTime(timezone=True), server_onupdate=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return u"Session ('{}','{}','{}')".format(self.identidad.login, self.ipaddr.ipaddr, self.useragent.parsed_ua)
|
||||
|
||||
class Conexion(db.Model):
|
||||
__tablename__ = 'conexiones'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), primary_key=True)
|
||||
|
||||
ipaddrid = db.Column(db.Integer, db.ForeignKey('registros.ipaddrs.id'), nullable=False)
|
||||
ipaddr = db.relationship('Ipaddr')
|
||||
|
||||
sesionid = db.Column(db.Integer, db.ForeignKey('registros.sesiones.id'), nullable=False)
|
||||
sesion = db.relationship('Sesion', back_populates='conexiones', lazy=True)
|
||||
|
||||
iniciada = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||
ultimo = db.Column(db.DateTime(timezone=True), server_onupdate=func.now())
|
||||
|
||||
__table_args__ = (db.UniqueConstraint('ipaddrid', 'sesionid', name='_una_ip_sesion_uc'),{ 'schema': 'registros' })
|
||||
|
||||
class Ubicacion(db.Model):
|
||||
__tablename__ = 'ubicaciones'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), primary_key=True)
|
||||
|
||||
ipaddrid = db.Column(db.Integer, db.ForeignKey('registros.ipaddrs.id'), nullable=False)
|
||||
ipaddr = db.relationship('Ipaddr')
|
||||
|
||||
identidadid = db.Column(db.Integer, db.ForeignKey('usuarios.identidades.id'), nullable=False)
|
||||
identidad = db.relationship('Identidad', back_populates='ubicaciones', lazy=True)
|
||||
|
||||
descripcion = db.Column(db.String(200), nullable=True)
|
||||
|
||||
iniciada = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||
ultimo = db.Column(db.DateTime(timezone=True), server_onupdate=func.now())
|
||||
|
||||
__table_args__ = (db.UniqueConstraint('ipaddrid', 'identidadid', name='_una_ip_identidad_uc'),{ 'schema': 'registros' })
|
||||
|
||||
class Ipaddr(db.Model):
|
||||
__tablename__ = 'ipaddrs'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
ipaddr = db.Column(db.String(15).with_variant(postgresql.INET(), 'postgresql'), unique=True, nullable=False)
|
||||
hostname = db.Column(db.String(100), nullable=True)
|
||||
|
||||
def __repr__(self):
|
||||
return "IpAddr('{}','{}')".format(self.ipaddr, self.hostname)
|
||||
|
||||
class Dispositivo(db.Model):
|
||||
__tablename__ = 'dispositivos'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
useragent = db.Column(db.String(1024), unique=True, nullable=False)
|
||||
parsed_ua = db.Column(db.String(150))
|
||||
dev = db.Column(db.String(50))
|
||||
os = db.Column(db.String(50))
|
||||
browser = db.Column(db.String(50))
|
||||
|
||||
def __repr__(self):
|
||||
return "Useragent('{}')".format(self.parsed_ua)
|
||||
|
||||
class Ruta(db.Model):
|
||||
__tablename__ = 'rutas'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
ruta = db.Column(db.String(100), unique=True, nullable=False)
|
||||
|
||||
def __repr__(self):
|
||||
return "Url('{}')".format(self.ruta)
|
||||
|
||||
class Registro(db.Model):
|
||||
__tablename__ = 'registros'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.BigInteger(), primary_key=True)
|
||||
|
||||
sitioid = db.Column(db.Integer, db.ForeignKey('registros.sitios.id'), nullable=False)
|
||||
sitio = db.relationship('Sitio')
|
||||
|
||||
rutaid = db.Column(db.Integer, db.ForeignKey('registros.rutas.id'), nullable=False)
|
||||
ruta = db.relationship('Ruta')
|
||||
|
||||
sesionid = db.Column(db.BigInteger().with_variant(sqlite.INTEGER(), 'sqlite'), db.ForeignKey('registros.sesiones.id'), nullable=False)
|
||||
sesion = db.relationship('Sesion')
|
||||
|
||||
ipaddrid = db.Column(db.Integer, db.ForeignKey('registros.ipaddrs.id'), nullable=False)
|
||||
ipaddr = db.relationship('Ipaddr')
|
||||
|
||||
tamano = db.Column(db.Integer)
|
||||
creado = db.Column(db.DateTime, server_default=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return "Registro('{}')".format(self.ruta.ruta)
|
||||
|
||||
class Sitio(db.Model):
|
||||
__tablename__ = 'sitios'
|
||||
__table_args__ = { 'schema': 'registros' }
|
||||
__bind_key__ = 'system'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
sitio = db.Column(db.String(100), unique=True, nullable=False)
|
|
@ -0,0 +1,28 @@
|
|||
Flask
|
||||
Flask-Bcrypt
|
||||
Flask-Login
|
||||
Flask-Mail
|
||||
Flask-SQLAlchemy
|
||||
Flask-WTF
|
||||
flask_migrate
|
||||
psycopg2-binary
|
||||
email_validator
|
||||
itsdangerous
|
||||
Jinja2
|
||||
MarkupSafe
|
||||
SQLAlchemy
|
||||
Werkzeug
|
||||
WTForms
|
||||
PyYAML
|
||||
ua-parser
|
||||
user-agents
|
||||
netaddr
|
||||
unidecode
|
||||
blinker
|
||||
certifi
|
||||
gunicorn
|
||||
bcrypt
|
||||
openpyxl
|
||||
fpdf
|
||||
Pillow
|
||||
PyPDF2<3
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
!function(a){a.fn.datepicker.dates.es={days:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],daysShort:["Dom","Lun","Mar","Mié","Jue","Vie","Sáb"],daysMin:["Do","Lu","Ma","Mi","Ju","Vi","Sa"],months:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],monthsShort:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],today:"Hoy",monthsTitle:"Meses",clear:"Borrar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,325 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v4.6.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors
|
||||
* Copyright 2011-2021 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus:not(:focus-visible) {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([class]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
button:not(:disabled),
|
||||
[type="button"]:not(:disabled),
|
||||
[type="reset"]:not(:disabled),
|
||||
[type="submit"]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,8 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v4.6.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors
|
||||
* Copyright 2011-2021 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
|
||||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="63pt" height="63pt" viewBox="0 0 63 39" version="1.1">
|
||||
|
||||
<circle cx="32" cy="20" r="25" stroke="black" stroke-width="2" fill="white" />
|
||||
|
||||
<defs>
|
||||
<g>
|
||||
<symbol overflow="visible" id="glyph0-0">
|
||||
<path style="stroke:none;" d=""/>
|
||||
</symbol>
|
||||
<symbol overflow="visible" id="glyph0-1">
|
||||
<path style="stroke:none;" d="M 7.90625 -15.4375 C 7.90625 -15.90625 7.5625 -16.1875 7.140625 -16.1875 C 6.6875 -16.1875 6.046875 -15.78125 6.046875 -15.09375 C 6.046875 -14.546875 6.4375 -14.34375 6.8125 -14.34375 C 7.3125 -14.34375 7.90625 -14.796875 7.90625 -15.4375 Z M 3.34375 -2.796875 C 3.140625 -2.25 3.140625 -1.9375 3.140625 -1.703125 C 3.140625 -0.265625 4.109375 0.25 4.9375 0.25 C 7.21875 0.25 8 -3.4375 8 -3.546875 C 8 -3.765625 7.78125 -3.765625 7.6875 -3.765625 C 7.390625 -3.765625 7.359375 -3.6875 7.265625 -3.328125 C 7.03125 -2.484375 6.390625 -0.25 4.984375 -0.25 C 4.703125 -0.25 4.40625 -0.375 4.40625 -0.96875 C 4.40625 -1.609375 4.765625 -2.5 5.203125 -3.71875 L 6.671875 -7.734375 C 6.890625 -8.34375 6.9375 -8.546875 6.9375 -9 C 6.9375 -10.265625 6.125 -10.921875 5.15625 -10.921875 C 2.875 -10.921875 2.0625 -7.265625 2.0625 -7.140625 C 2.0625 -6.90625 2.28125 -6.90625 2.40625 -6.90625 C 2.703125 -6.90625 2.71875 -6.984375 2.828125 -7.328125 C 3.015625 -8.125 3.671875 -10.4375 5.109375 -10.4375 C 5.65625 -10.4375 5.65625 -9.96875 5.65625 -9.6875 C 5.65625 -9.046875 5.5 -8.625 5.328125 -8.171875 Z M 3.34375 -2.796875 "/>
|
||||
</symbol>
|
||||
<symbol overflow="visible" id="glyph1-0">
|
||||
<path style="stroke:none;" d=""/>
|
||||
</symbol>
|
||||
<symbol overflow="visible" id="glyph1-1">
|
||||
<path style="stroke:none;" d="M 6.46875 -1.609375 C 6.171875 -1.609375 5.875 -1.59375 5.578125 -1.59375 L 4.265625 -1.59375 L 4.265625 -17.203125 L 2.1875 -17.203125 L 2.1875 0 L 11.625 0 L 11.625 -1.609375 Z M 6.46875 -1.609375 "/>
|
||||
</symbol>
|
||||
<symbol overflow="visible" id="glyph1-2">
|
||||
<path style="stroke:none;" d="M 9.3125 -7.015625 C 9.3125 -9.34375 7.65625 -10.953125 5.421875 -10.953125 C 4.34375 -10.953125 3.140625 -10.75 1.78125 -9.96875 L 1.9375 -8.328125 C 2.546875 -8.765625 3.625 -9.484375 5.40625 -9.484375 C 6.671875 -9.484375 7.4375 -8.53125 7.4375 -6.984375 L 7.4375 -6.046875 C 3.46875 -5.921875 0.984375 -4.78125 0.984375 -2.828125 C 0.984375 -1.8125 1.609375 0.203125 3.671875 0.203125 C 4.046875 0.203125 6.046875 0.15625 7.484375 -0.921875 L 7.484375 0 L 9.3125 0 Z M 7.4375 -3.265625 C 7.4375 -2.828125 7.4375 -2.234375 6.6875 -1.78125 C 6.046875 -1.359375 5.234375 -1.3125 4.875 -1.3125 C 3.640625 -1.3125 2.796875 -2 2.796875 -2.875 C 2.796875 -4.609375 6.671875 -4.78125 7.4375 -4.8125 Z M 7.4375 -3.265625 "/>
|
||||
</symbol>
|
||||
<symbol overflow="visible" id="glyph1-3">
|
||||
<path style="stroke:none;" d="M 3.6875 -17.203125 L 1.859375 -17.203125 L 1.859375 0 L 3.734375 0 L 3.734375 -1.109375 C 4.6875 -0.203125 5.828125 0.203125 6.84375 0.203125 C 9.21875 0.203125 11.15625 -2.203125 11.15625 -5.359375 C 11.15625 -8.234375 9.671875 -10.859375 7.453125 -10.859375 C 5.890625 -10.859375 4.515625 -10.203125 3.6875 -9.546875 Z M 3.734375 -8.078125 C 4.28125 -8.765625 5.0625 -9.34375 6.140625 -9.34375 C 7.609375 -9.34375 9.265625 -8.28125 9.265625 -5.359375 C 9.265625 -2.234375 7.28125 -1.3125 5.921875 -1.3125 C 5.421875 -1.3125 4.90625 -1.4375 4.40625 -1.859375 C 3.734375 -2.453125 3.734375 -2.828125 3.734375 -3.171875 Z M 3.734375 -8.078125 "/>
|
||||
</symbol>
|
||||
</g>
|
||||
</defs>
|
||||
<g id="surface1">
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-1" x="8.151" y="27.906"/>
|
||||
</g>
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph1-1" x="18.068" y="27.906"/>
|
||||
<use xlink:href="#glyph1-2" x="30.679676" y="27.906"/>
|
||||
<use xlink:href="#glyph1-3" x="41.85618" y="27.906"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue