add user-management
This commit is contained in:
@@ -1,76 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Bon de commande</title>
|
|
||||||
<link href="style.css" rel="stylesheet" />
|
|
||||||
<meta name="author" content="EDUSEG® <https://eduseg.com.br>" />
|
|
||||||
<meta name="dcterms.created" content="{{ dcterms.created }}" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<section id="cover">
|
|
||||||
<img src="eduseg.svg" style="width: 14rem" />
|
|
||||||
|
|
||||||
<p>Certificamos que</p>
|
|
||||||
<h1>{{ name }}</h1>
|
|
||||||
<p>
|
|
||||||
Portador(a) do CPF <strong>{{ cpf }} </strong>, concluiu o curso
|
|
||||||
de <strong>NR-10 Complementar (SEP)</strong> com aproveitamento
|
|
||||||
de
|
|
||||||
<strong>{{ progress }}%</strong>
|
|
||||||
</p>
|
|
||||||
<p>Realizado entre {{ start_date }} e {{ finish_date }}</p>
|
|
||||||
<p>Florianópolis, SC, {{ today }}</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="back">
|
|
||||||
<div class="space-y">
|
|
||||||
<h1>Conteúdo programático ministrado</h1>
|
|
||||||
<ul>
|
|
||||||
<li>Organização do sistema elétrico de potência</li>
|
|
||||||
<li>Organização do trabalho</li>
|
|
||||||
<li>Aspectos comportamentais</li>
|
|
||||||
<li>Condições impeditivas para serviços</li>
|
|
||||||
<li>Riscos típicos no SEP e sua prevenção</li>
|
|
||||||
<li>Técnicas de análise de riscos no SEP</li>
|
|
||||||
<li>Procedimentos de trabalho (análise e discussão)</li>
|
|
||||||
<li>Técnicas de análise de riscos no SEP</li>
|
|
||||||
<li>Equipamentos e ferramentas de trabalho</li>
|
|
||||||
<li>Sistemas de proteção coletiva</li>
|
|
||||||
<li>Equipamentos de proteção individual</li>
|
|
||||||
<li>Posturas e vestuários de trabalhos</li>
|
|
||||||
<li>
|
|
||||||
Segurança com veículos e transporte de pessoas,
|
|
||||||
materiais e equipamentos
|
|
||||||
</li>
|
|
||||||
<li>Sinalização e isolamento de áreas de trabalho</li>
|
|
||||||
<li>
|
|
||||||
Liberação de instalação para serviço, operação e uso
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Treinamento em técnicas de remoção, atendimento e
|
|
||||||
transporte de acidentados
|
|
||||||
</li>
|
|
||||||
<li>Acidentes típicos</li>
|
|
||||||
<li>Responsabilidades</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y">
|
|
||||||
<dd>
|
|
||||||
<h2>Carga horária</h2>
|
|
||||||
<p>40 horas</p>
|
|
||||||
</dd>
|
|
||||||
|
|
||||||
<dd>
|
|
||||||
<h2>Instrutor e responsável técnico</h2>
|
|
||||||
<p>Francis Ricardo Baretta</p>
|
|
||||||
<p>CPF 039.539.409-02</p>
|
|
||||||
<p>Eng. de Segurança no Trabalho Eng. Eletricista</p>
|
|
||||||
<p>CREA/SC 126693-0</p>
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
0
certs/conf.py
Normal file
0
certs/conf.py
Normal file
@@ -1,59 +0,0 @@
|
|||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 1072.73 329.6"
|
|
||||||
>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path
|
|
||||||
fill="#8cd366"
|
|
||||||
d="M152.18,217.62l-68.61-30.91c-1.88-1.2-4.28-1.2-6.16,0l-68.61,30.91c-3.81,2.43-8.8-.3-8.8-4.82V8.98C0,5.82,2.56,3.26,5.72,3.26h149.54c3.16,0,5.72,2.56,5.72,5.72v203.81c0,4.52-5,7.26-8.8,4.82Z"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
fill="#2e3524"
|
|
||||||
d="M93.97,74.01H26.61v20.16h67.36v-20.16Z"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
fill="#2e3524"
|
|
||||||
d="M107.44,111.16H26.61v26.8h80.83v-26.8Z"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
fill="#2e3524"
|
|
||||||
d="M107.44,30.27H26.61v26.8h80.83v-26.8Z"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
fill="#2e3524"
|
|
||||||
d="M134.38,131.23c0-3.72-3.02-6.73-6.73-6.73s-6.73,3.02-6.73,6.73,3.02,6.73,6.73,6.73,6.73-3.02,6.73-6.73Z"
|
|
||||||
></path>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path
|
|
||||||
fill="#f9f7e8"
|
|
||||||
d="M244.7,3.24h92.33v44.43h-44.15v88.85h39.38v39.62h-39.38v105.77h44.15v44.42h-92.33V3.24Z"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
fill="#f9f7e8"
|
|
||||||
d="M362.72,3.24h57.79c10.71,0,20.47,2.35,29.29,7.06,8.83,4.7,15.79,11.18,20.87,19.39,5.08,8.21,7.63,17.45,7.63,27.67v214.88c0,10.22-2.48,19.46-7.42,27.67-4.96,8.21-11.83,14.69-20.68,19.39-8.83,4.7-18.73,7.08-29.7,7.08h-57.79V3.24ZM427.55,283.88c1.74-1.87,2.6-4.18,2.6-6.86V52.56c-.26-2.69-1.34-4.97-3.22-6.86-1.88-1.87-4.15-2.83-6.82-2.83h-14v243.85h14.41c2.93,0,5.27-.94,7.01-2.83h.02Z"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
fill="#f9f7e8"
|
|
||||||
d="M531.5,322.49c-8.71-4.7-15.6-11.16-20.68-19.39-5.08-8.21-7.63-17.42-7.63-27.67V3.24h48.15v279.41c0,2.69.93,4.99,2.82,6.86,1.86,1.9,4.15,2.83,6.82,2.83,2.93,0,5.27-.94,7.01-2.83,1.74-1.87,2.6-4.18,2.6-6.86V3.24h48.16v272.21c0,10.25-2.48,19.46-7.42,27.67-4.96,8.21-11.83,14.69-20.68,19.39-8.85,4.7-18.73,7.08-29.7,7.08s-20.8-2.35-29.5-7.08l.05-.02Z"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
fill="#f9f7e8"
|
|
||||||
d="M672.79,322.49c-8.7-4.7-15.6-11.16-20.68-19.39-5.08-8.21-7.63-17.42-7.63-27.67v-78.75h48.16v85.95c0,2.69.93,4.99,2.82,6.87,1.86,1.9,4.15,2.83,6.82,2.83,2.93,0,5.27-.94,7.01-2.83,1.74-1.87,2.6-4.18,2.6-6.87v-77.88c0-5.66-2.22-10.3-6.63-13.94-4.41-3.62-11.57-8.02-21.47-13.13-8.3-4.3-15.05-8.14-20.27-11.52-5.22-3.36-9.71-7.87-13.45-13.54-3.75-5.66-5.63-12.24-5.63-19.8V54.12c0-10.22,2.53-19.44,7.63-27.67,5.08-8.21,11.97-14.66,20.68-19.39,8.68-4.7,18.53-7.06,29.5-7.06s20.87,2.35,29.69,7.06c8.83,4.7,15.72,11.18,20.68,19.39,4.96,8.21,7.42,17.45,7.42,27.67v71.09h-48.16V46.92c0-2.69-.88-4.97-2.6-6.86-1.74-1.87-4.08-2.83-7.01-2.83-2.67,0-4.96.94-6.82,2.83-1.89,1.9-2.82,4.18-2.82,6.86v69.79c0,6.19,2.34,11.26,7.04,15.14,4.67,3.91,12.24,8.83,22.68,14.74,8.04,4.32,14.57,8.09,19.68,11.3,5.08,3.24,9.37,7.46,12.83,12.72,3.48,5.26,5.22,11.26,5.22,17.98v86.83c0,10.25-2.48,19.46-7.42,27.67-4.96,8.21-11.83,14.69-20.68,19.39-8.85,4.71-18.72,7.08-29.7,7.08s-20.8-2.35-29.5-7.08Z"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
fill="#f9f7e8"
|
|
||||||
d="M784.56,3.24h92.33v44.43h-44.15v88.85h39.38v39.62h-39.38v105.77h44.15v44.42h-92.33V3.24Z"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
fill="#f9f7e8"
|
|
||||||
d="M920.63,322.49c-5.63-4.18-10.11-10.1-13.45-17.76-3.34-7.68-5.01-16.49-5.01-26.45V53.71c0-9.96,2.53-19.06,7.63-27.26,5.08-8.21,12.05-14.66,20.87-19.39,8.83-4.7,18.6-7.06,29.32-7.06s20.54,2.35,29.5,7.06c8.97,4.7,15.91,11.18,20.87,19.39,4.96,8.21,7.42,17.3,7.42,27.26v94.51h-48.16V46.92c0-2.69-.88-4.97-2.6-6.86-1.74-1.87-4.08-2.83-7.01-2.83-2.67,0-4.96.94-6.82,2.83-1.89,1.9-2.82,4.18-2.82,6.86v231.36c0,2.69.93,4.99,2.82,6.87,1.86,1.9,4.15,2.83,6.82,2.83,2.93,0,5.27-.94,7.01-2.83,1.74-1.87,2.6-4.18,2.6-6.87v-46.03h-11.64v-51.29h59.8v145.4h-48.16v-14.14c-2.96,5.4-6.82,9.48-11.64,12.31-4.82,2.83-10.83,4.25-18.06,4.25s-13.64-2.09-19.27-6.26l-.02-.02Z"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
fill="#f9f7e8"
|
|
||||||
d="M1053.27,25.05h-6.13l-.06-3.69h5.48c.83-.02,1.61-.15,2.33-.4.72-.27,1.3-.64,1.73-1.14.44-.51.65-1.14.65-1.87,0-.93-.16-1.67-.48-2.22-.3-.55-.83-.94-1.59-1.16-.74-.25-1.74-.37-3.01-.37h-3.78v20.42h-4.12V10.54h7.9c1.87,0,3.49.27,4.86.82,1.38.53,2.44,1.34,3.18,2.44.76,1.08,1.14,2.43,1.14,4.06,0,1.02-.24,1.93-.71,2.73-.47.8-1.17,1.49-2.1,2.07-.91.57-2.03,1.03-3.35,1.39-.06,0-.12.07-.2.2-.06.13-.11.2-.17.2-.32.19-.53.33-.63.43-.08.08-.16.12-.25.14-.08.02-.31.03-.68.03ZM1052.99,25.05l.6-2.81c2.95,0,4.97.64,6.05,1.93,1.08,1.27,1.62,2.89,1.62,4.86v1.53c0,.7.03,1.37.08,2.02.08.62.21,1.15.4,1.59v.45h-4.23c-.19-.49-.3-1.19-.34-2.1-.02-.91-.03-1.57-.03-1.99v-1.48c0-1.38-.31-2.39-.94-3.04s-1.69-.97-3.21-.97ZM1035.75,22.92c0,2.52.43,4.87,1.28,7.04.87,2.16,2.08,4.05,3.64,5.68,1.55,1.61,3.34,2.87,5.37,3.78,2.05.89,4.22,1.33,6.53,1.33s4.51-.44,6.53-1.33c2.03-.91,3.8-2.17,5.34-3.78,1.53-1.63,2.74-3.52,3.61-5.68.87-2.18,1.31-4.52,1.31-7.04s-.44-4.86-1.31-7.01c-.87-2.16-2.07-4.04-3.61-5.65-1.53-1.61-3.31-2.86-5.34-3.75-2.03-.91-4.2-1.36-6.53-1.36s-4.49.45-6.53,1.36c-2.03.89-3.81,2.14-5.37,3.75-1.55,1.61-2.77,3.49-3.64,5.65-.85,2.16-1.28,4.5-1.28,7.01ZM1032.4,22.92c0-3.01.52-5.8,1.56-8.38,1.04-2.57,2.49-4.82,4.34-6.73,1.86-1.93,4-3.43,6.42-4.49,2.44-1.08,5.06-1.62,7.84-1.62s5.39.54,7.81,1.62c2.44,1.06,4.58,2.56,6.42,4.49,1.86,1.91,3.31,4.16,4.35,6.73,1.06,2.57,1.59,5.37,1.59,8.38s-.53,5.8-1.59,8.38c-1.04,2.57-2.49,4.84-4.35,6.79-1.83,1.93-3.97,3.44-6.42,4.52-2.42,1.08-5.03,1.62-7.81,1.62s-5.39-.54-7.84-1.62c-2.42-1.08-4.56-2.58-6.42-4.52-1.85-1.95-3.3-4.21-4.34-6.79-1.04-2.57-1.56-5.37-1.56-8.38Z"
|
|
||||||
></path>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 5.6 KiB |
@@ -8,7 +8,7 @@ from weasyprint import HTML
|
|||||||
locale.setlocale(locale.LC_TIME, 'pt_BR')
|
locale.setlocale(locale.LC_TIME, 'pt_BR')
|
||||||
today = date.today()
|
today = date.today()
|
||||||
|
|
||||||
with open('cert.html', encoding='utf-8') as f:
|
with open('nr10_complementar_sep.html', encoding='utf-8') as f:
|
||||||
html = f.read()
|
html = f.read()
|
||||||
|
|
||||||
|
|
||||||
@@ -25,9 +25,8 @@ html_rendered = template.render(
|
|||||||
progress=91.99,
|
progress=91.99,
|
||||||
course='NR-10 Complementar (SEP)',
|
course='NR-10 Complementar (SEP)',
|
||||||
today=today.strftime('%-d de %B de %Y'),
|
today=today.strftime('%-d de %B de %Y'),
|
||||||
start_date=today.strftime('%d/%m/%Y'),
|
started_date=today.strftime('%d/%m/%Y'),
|
||||||
finish_date=today.strftime('%d/%m/%Y'),
|
finished_date=today.strftime('%d/%m/%Y'),
|
||||||
due_date=today.strftime('%d/%m/%Y'),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
HTML(string=html_rendered, base_url='').write_pdf('cert.pdf')
|
HTML(string=html_rendered, base_url='').write_pdf('cert.pdf')
|
||||||
|
|||||||
212
certs/nr10_complementar_sep.html
Normal file
212
certs/nr10_complementar_sep.html
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>NR-10 Complementar (SEP)</title>
|
||||||
|
<link href="style.css" rel="stylesheet" />
|
||||||
|
<meta name="author" content="EDUSEG® <https://eduseg.com.br>" />
|
||||||
|
<meta name="dcterms.created" content="{{ dcterms.created }}" />
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
div,
|
||||||
|
h1,
|
||||||
|
p,
|
||||||
|
a {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "SF-Pro";
|
||||||
|
src: url("fonts/SF-Pro.ttf") format("truetype");
|
||||||
|
}
|
||||||
|
|
||||||
|
@page {
|
||||||
|
size: A4 landscape;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: SF-Pro;
|
||||||
|
font-size: 13pt;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
width: 29.7cm;
|
||||||
|
height: 21cm;
|
||||||
|
break-after: page;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cover {
|
||||||
|
background-color: #a7e400;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cover h1 {
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 24pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
#back {
|
||||||
|
background-color: white;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#back h1,
|
||||||
|
#back h2 {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
#back ul {
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-y > :not(:last-child) {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<section id="cover">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 1072.73 329.6"
|
||||||
|
style="width: 14rem"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
fill="#8cd366"
|
||||||
|
d="M152.18,217.62l-68.61-30.91c-1.88-1.2-4.28-1.2-6.16,0l-68.61,30.91c-3.81,2.43-8.8-.3-8.8-4.82V8.98C0,5.82,2.56,3.26,5.72,3.26h149.54c3.16,0,5.72,2.56,5.72,5.72v203.81c0,4.52-5,7.26-8.8,4.82Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#2e3524"
|
||||||
|
d="M93.97,74.01H26.61v20.16h67.36v-20.16Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#2e3524"
|
||||||
|
d="M107.44,111.16H26.61v26.8h80.83v-26.8Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#2e3524"
|
||||||
|
d="M107.44,30.27H26.61v26.8h80.83v-26.8Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#2e3524"
|
||||||
|
d="M134.38,131.23c0-3.72-3.02-6.73-6.73-6.73s-6.73,3.02-6.73,6.73,3.02,6.73,6.73,6.73,6.73-3.02,6.73-6.73Z"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
fill="#f9f7e8"
|
||||||
|
d="M244.7,3.24h92.33v44.43h-44.15v88.85h39.38v39.62h-39.38v105.77h44.15v44.42h-92.33V3.24Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#f9f7e8"
|
||||||
|
d="M362.72,3.24h57.79c10.71,0,20.47,2.35,29.29,7.06,8.83,4.7,15.79,11.18,20.87,19.39,5.08,8.21,7.63,17.45,7.63,27.67v214.88c0,10.22-2.48,19.46-7.42,27.67-4.96,8.21-11.83,14.69-20.68,19.39-8.83,4.7-18.73,7.08-29.7,7.08h-57.79V3.24ZM427.55,283.88c1.74-1.87,2.6-4.18,2.6-6.86V52.56c-.26-2.69-1.34-4.97-3.22-6.86-1.88-1.87-4.15-2.83-6.82-2.83h-14v243.85h14.41c2.93,0,5.27-.94,7.01-2.83h.02Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#f9f7e8"
|
||||||
|
d="M531.5,322.49c-8.71-4.7-15.6-11.16-20.68-19.39-5.08-8.21-7.63-17.42-7.63-27.67V3.24h48.15v279.41c0,2.69.93,4.99,2.82,6.86,1.86,1.9,4.15,2.83,6.82,2.83,2.93,0,5.27-.94,7.01-2.83,1.74-1.87,2.6-4.18,2.6-6.86V3.24h48.16v272.21c0,10.25-2.48,19.46-7.42,27.67-4.96,8.21-11.83,14.69-20.68,19.39-8.85,4.7-18.73,7.08-29.7,7.08s-20.8-2.35-29.5-7.08l.05-.02Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#f9f7e8"
|
||||||
|
d="M672.79,322.49c-8.7-4.7-15.6-11.16-20.68-19.39-5.08-8.21-7.63-17.42-7.63-27.67v-78.75h48.16v85.95c0,2.69.93,4.99,2.82,6.87,1.86,1.9,4.15,2.83,6.82,2.83,2.93,0,5.27-.94,7.01-2.83,1.74-1.87,2.6-4.18,2.6-6.87v-77.88c0-5.66-2.22-10.3-6.63-13.94-4.41-3.62-11.57-8.02-21.47-13.13-8.3-4.3-15.05-8.14-20.27-11.52-5.22-3.36-9.71-7.87-13.45-13.54-3.75-5.66-5.63-12.24-5.63-19.8V54.12c0-10.22,2.53-19.44,7.63-27.67,5.08-8.21,11.97-14.66,20.68-19.39,8.68-4.7,18.53-7.06,29.5-7.06s20.87,2.35,29.69,7.06c8.83,4.7,15.72,11.18,20.68,19.39,4.96,8.21,7.42,17.45,7.42,27.67v71.09h-48.16V46.92c0-2.69-.88-4.97-2.6-6.86-1.74-1.87-4.08-2.83-7.01-2.83-2.67,0-4.96.94-6.82,2.83-1.89,1.9-2.82,4.18-2.82,6.86v69.79c0,6.19,2.34,11.26,7.04,15.14,4.67,3.91,12.24,8.83,22.68,14.74,8.04,4.32,14.57,8.09,19.68,11.3,5.08,3.24,9.37,7.46,12.83,12.72,3.48,5.26,5.22,11.26,5.22,17.98v86.83c0,10.25-2.48,19.46-7.42,27.67-4.96,8.21-11.83,14.69-20.68,19.39-8.85,4.71-18.72,7.08-29.7,7.08s-20.8-2.35-29.5-7.08Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#f9f7e8"
|
||||||
|
d="M784.56,3.24h92.33v44.43h-44.15v88.85h39.38v39.62h-39.38v105.77h44.15v44.42h-92.33V3.24Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#f9f7e8"
|
||||||
|
d="M920.63,322.49c-5.63-4.18-10.11-10.1-13.45-17.76-3.34-7.68-5.01-16.49-5.01-26.45V53.71c0-9.96,2.53-19.06,7.63-27.26,5.08-8.21,12.05-14.66,20.87-19.39,8.83-4.7,18.6-7.06,29.32-7.06s20.54,2.35,29.5,7.06c8.97,4.7,15.91,11.18,20.87,19.39,4.96,8.21,7.42,17.3,7.42,27.26v94.51h-48.16V46.92c0-2.69-.88-4.97-2.6-6.86-1.74-1.87-4.08-2.83-7.01-2.83-2.67,0-4.96.94-6.82,2.83-1.89,1.9-2.82,4.18-2.82,6.86v231.36c0,2.69.93,4.99,2.82,6.87,1.86,1.9,4.15,2.83,6.82,2.83,2.93,0,5.27-.94,7.01-2.83,1.74-1.87,2.6-4.18,2.6-6.87v-46.03h-11.64v-51.29h59.8v145.4h-48.16v-14.14c-2.96,5.4-6.82,9.48-11.64,12.31-4.82,2.83-10.83,4.25-18.06,4.25s-13.64-2.09-19.27-6.26l-.02-.02Z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#f9f7e8"
|
||||||
|
d="M1053.27,25.05h-6.13l-.06-3.69h5.48c.83-.02,1.61-.15,2.33-.4.72-.27,1.3-.64,1.73-1.14.44-.51.65-1.14.65-1.87,0-.93-.16-1.67-.48-2.22-.3-.55-.83-.94-1.59-1.16-.74-.25-1.74-.37-3.01-.37h-3.78v20.42h-4.12V10.54h7.9c1.87,0,3.49.27,4.86.82,1.38.53,2.44,1.34,3.18,2.44.76,1.08,1.14,2.43,1.14,4.06,0,1.02-.24,1.93-.71,2.73-.47.8-1.17,1.49-2.1,2.07-.91.57-2.03,1.03-3.35,1.39-.06,0-.12.07-.2.2-.06.13-.11.2-.17.2-.32.19-.53.33-.63.43-.08.08-.16.12-.25.14-.08.02-.31.03-.68.03ZM1052.99,25.05l.6-2.81c2.95,0,4.97.64,6.05,1.93,1.08,1.27,1.62,2.89,1.62,4.86v1.53c0,.7.03,1.37.08,2.02.08.62.21,1.15.4,1.59v.45h-4.23c-.19-.49-.3-1.19-.34-2.1-.02-.91-.03-1.57-.03-1.99v-1.48c0-1.38-.31-2.39-.94-3.04s-1.69-.97-3.21-.97ZM1035.75,22.92c0,2.52.43,4.87,1.28,7.04.87,2.16,2.08,4.05,3.64,5.68,1.55,1.61,3.34,2.87,5.37,3.78,2.05.89,4.22,1.33,6.53,1.33s4.51-.44,6.53-1.33c2.03-.91,3.8-2.17,5.34-3.78,1.53-1.63,2.74-3.52,3.61-5.68.87-2.18,1.31-4.52,1.31-7.04s-.44-4.86-1.31-7.01c-.87-2.16-2.07-4.04-3.61-5.65-1.53-1.61-3.31-2.86-5.34-3.75-2.03-.91-4.2-1.36-6.53-1.36s-4.49.45-6.53,1.36c-2.03.89-3.81,2.14-5.37,3.75-1.55,1.61-2.77,3.49-3.64,5.65-.85,2.16-1.28,4.5-1.28,7.01ZM1032.4,22.92c0-3.01.52-5.8,1.56-8.38,1.04-2.57,2.49-4.82,4.34-6.73,1.86-1.93,4-3.43,6.42-4.49,2.44-1.08,5.06-1.62,7.84-1.62s5.39.54,7.81,1.62c2.44,1.06,4.58,2.56,6.42,4.49,1.86,1.91,3.31,4.16,4.35,6.73,1.06,2.57,1.59,5.37,1.59,8.38s-.53,5.8-1.59,8.38c-1.04,2.57-2.49,4.84-4.35,6.79-1.83,1.93-3.97,3.44-6.42,4.52-2.42,1.08-5.03,1.62-7.81,1.62s-5.39-.54-7.84-1.62c-2.42-1.08-4.56-2.58-6.42-4.52-1.85-1.95-3.3-4.21-4.34-6.79-1.04-2.57-1.56-5.37-1.56-8.38Z"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<p>Certificamos que</p>
|
||||||
|
<h1>{{ name }}</h1>
|
||||||
|
<p>
|
||||||
|
Portador(a) do CPF <strong>{{ cpf }} </strong>, concluiu o curso
|
||||||
|
de <strong>NR-10 Complementar (SEP)</strong> com aproveitamento
|
||||||
|
de
|
||||||
|
<strong>{{ progress }}%</strong>
|
||||||
|
</p>
|
||||||
|
<p>Realizado entre {{ start_date }} e {{ finish_date }}</p>
|
||||||
|
<p>Florianópolis, SC, {{ today }}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="back">
|
||||||
|
<div class="space-y">
|
||||||
|
<h1>Conteúdo programático ministrado</h1>
|
||||||
|
<ul>
|
||||||
|
<li>Organização do sistema elétrico de potência</li>
|
||||||
|
<li>Organização do trabalho</li>
|
||||||
|
<li>Aspectos comportamentais</li>
|
||||||
|
<li>Condições impeditivas para serviços</li>
|
||||||
|
<li>Riscos típicos no SEP e sua prevenção</li>
|
||||||
|
<li>Técnicas de análise de riscos no SEP</li>
|
||||||
|
<li>Procedimentos de trabalho (análise e discussão)</li>
|
||||||
|
<li>Técnicas de análise de riscos no SEP</li>
|
||||||
|
<li>Equipamentos e ferramentas de trabalho</li>
|
||||||
|
<li>Sistemas de proteção coletiva</li>
|
||||||
|
<li>Equipamentos de proteção individual</li>
|
||||||
|
<li>Posturas e vestuários de trabalhos</li>
|
||||||
|
<li>
|
||||||
|
Segurança com veículos e transporte de pessoas,
|
||||||
|
materiais e equipamentos
|
||||||
|
</li>
|
||||||
|
<li>Sinalização e isolamento de áreas de trabalho</li>
|
||||||
|
<li>
|
||||||
|
Liberação de instalação para serviço, operação e uso
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Treinamento em técnicas de remoção, atendimento e
|
||||||
|
transporte de acidentados
|
||||||
|
</li>
|
||||||
|
<li>Acidentes típicos</li>
|
||||||
|
<li>Responsabilidades</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y">
|
||||||
|
<dd>
|
||||||
|
<h2>Carga horária</h2>
|
||||||
|
<p>40 horas</p>
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<dd>
|
||||||
|
<h2>Instrutor e responsável técnico</h2>
|
||||||
|
<p>Francis Ricardo Baretta</p>
|
||||||
|
<p>CPF 039.539.409-02</p>
|
||||||
|
<p>Eng. de Segurança no Trabalho Eng. Eletricista</p>
|
||||||
|
<p>CREA/SC 126693-0</p>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/* http://meyerweb.com/eric/tools/css/reset/
|
|
||||||
v2.0 | 20110126
|
|
||||||
License: none (public domain)
|
|
||||||
*/
|
|
||||||
html,
|
|
||||||
body,
|
|
||||||
div,
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6,
|
|
||||||
p,
|
|
||||||
a {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
font: inherit;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "SF-Pro";
|
|
||||||
src: url("fonts/SF-Pro.ttf") format("truetype");
|
|
||||||
}
|
|
||||||
|
|
||||||
@page {
|
|
||||||
size: A4 landscape;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-family: SF-Pro;
|
|
||||||
font-size: 13pt;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
width: 29.7cm;
|
|
||||||
height: 21cm;
|
|
||||||
break-after: page;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 5rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cover {
|
|
||||||
background-color: #a7e400;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cover h1 {
|
|
||||||
font-weight: bolder;
|
|
||||||
font-size: 24pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
#back {
|
|
||||||
background-color: white;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#back h1,
|
|
||||||
#back h2 {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 16pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
#back ul {
|
|
||||||
padding-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.space-y > :not(:last-child) {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
5
enrollment-management/app/config.py
Normal file
5
enrollment-management/app/config.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
USER_TABLE: str = os.getenv('USER_TABLE') # type: ignore
|
||||||
|
ORDER_TABLE: str = os.getenv('ORDER_TABLE') # type: ignore
|
||||||
|
ENROLLMENT_TABLE: str = os.getenv('ENROLLMENT_TABLE') # type: ignore
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from aws_lambda_powertools import Logger
|
||||||
|
from aws_lambda_powertools.utilities.data_classes import (
|
||||||
|
EventBridgeEvent,
|
||||||
|
event_source,
|
||||||
|
)
|
||||||
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
|
|
||||||
|
logger = Logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@event_source(data_class=EventBridgeEvent)
|
||||||
|
@logger.inject_lambda_context
|
||||||
|
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> None:
|
||||||
|
"""Send an email when the access period expires"""
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from aws_lambda_powertools import Logger
|
||||||
|
from aws_lambda_powertools.utilities.data_classes import (
|
||||||
|
EventBridgeEvent,
|
||||||
|
event_source,
|
||||||
|
)
|
||||||
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
|
|
||||||
|
logger = Logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@event_source(data_class=EventBridgeEvent)
|
||||||
|
@logger.inject_lambda_context
|
||||||
|
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> None: ...
|
||||||
48
enrollment-management/app/events/set_status_as_expired.py
Normal file
48
enrollment-management/app/events/set_status_as_expired.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
from aws_lambda_powertools import Logger
|
||||||
|
from aws_lambda_powertools.utilities.data_classes import (
|
||||||
|
EventBridgeEvent,
|
||||||
|
event_source,
|
||||||
|
)
|
||||||
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
|
from layercake.dateutils import now
|
||||||
|
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
|
||||||
|
|
||||||
|
from boto3clients import dynamodb_client
|
||||||
|
from config import ENROLLMENT_TABLE
|
||||||
|
|
||||||
|
logger = Logger(__name__)
|
||||||
|
enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
|
||||||
|
|
||||||
|
|
||||||
|
@event_source(data_class=EventBridgeEvent)
|
||||||
|
@logger.inject_lambda_context
|
||||||
|
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||||
|
new_image = event.detail['new_image']
|
||||||
|
now_ = now()
|
||||||
|
|
||||||
|
with enrollment_layer.transact_items() as transact:
|
||||||
|
transact.update(
|
||||||
|
key=KeyPair(new_image['id'], '0'),
|
||||||
|
update_expr='SET #status = :expired, update_date = :update_date',
|
||||||
|
cond_expr='#status IN (:pending, :in_progress)',
|
||||||
|
expr_attr_names={
|
||||||
|
'#status': 'status',
|
||||||
|
},
|
||||||
|
expr_attr_values={
|
||||||
|
':expired': 'EXPIRED',
|
||||||
|
':pending': 'PENDING',
|
||||||
|
':in_progress': 'IN_PROGRESS',
|
||||||
|
':update_date': now_,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
transact.put(
|
||||||
|
item={
|
||||||
|
'id': new_image['id'],
|
||||||
|
'sk': 'expired_date',
|
||||||
|
'create_date': now_,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
transact.write_items()
|
||||||
|
|
||||||
|
return True
|
||||||
5
order-management/app/config.py
Normal file
5
order-management/app/config.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
USER_TABLE: str = os.getenv('USER_TABLE') # type: ignore
|
||||||
|
ORDER_TABLE: str = os.getenv('ORDER_TABLE') # type: ignore
|
||||||
|
ENROLLMENT_TABLE: str = os.getenv('ENROLLMENT_TABLE') # type: ignore
|
||||||
5
user-management/Makefile
Normal file
5
user-management/Makefile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
build:
|
||||||
|
sam build --use-container
|
||||||
|
|
||||||
|
deploy: build
|
||||||
|
sam deploy --debug
|
||||||
3
user-management/app/boto3clients.py
Normal file
3
user-management/app/boto3clients.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import boto3
|
||||||
|
|
||||||
|
s3_client = boto3.client('s3')
|
||||||
1
user-management/app/config.py
Normal file
1
user-management/app/config.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CHUNK_SIZE = 50
|
||||||
83
user-management/app/csv_utils.py
Normal file
83
user-management/app/csv_utils.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import csv
|
||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
from smart_open import open
|
||||||
|
|
||||||
|
|
||||||
|
def byte_ranges(
|
||||||
|
csvfile: str,
|
||||||
|
chunk_size: int = 100,
|
||||||
|
**kwargs,
|
||||||
|
) -> list[tuple[int, int]]:
|
||||||
|
"""Compute byte ranges for reading a CSV file in fixed-size line chunks.
|
||||||
|
|
||||||
|
Returns pairs (start_byte, end_byte) for each fixed-size group of lines.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
csvfile : str
|
||||||
|
Path to the CSV file, opened in binary mode internally.
|
||||||
|
chunk_size : int, optional
|
||||||
|
Number of lines per chunk. Default is 100.
|
||||||
|
**kwargs :
|
||||||
|
Extra options passed to `open()`, e.g., buffering.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
list of tuple[int, int]
|
||||||
|
Byte ranges covering each chunk of lines.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> byte_ranges("users.csv", chunk_size=500)
|
||||||
|
[(0, 3125), (3126, 6150), (6151, 9124)]
|
||||||
|
"""
|
||||||
|
line_offsets = [0]
|
||||||
|
|
||||||
|
with open(csvfile, 'rb', **kwargs) as fp:
|
||||||
|
while True:
|
||||||
|
if not fp.readline():
|
||||||
|
break
|
||||||
|
line_offsets.append(fp.tell())
|
||||||
|
|
||||||
|
total_lines = len(line_offsets) - 1
|
||||||
|
byte_ranges = []
|
||||||
|
|
||||||
|
for start_line in range(1, total_lines + 1, chunk_size):
|
||||||
|
# Calculate the end line index, bounded by total lines
|
||||||
|
end_line = min(start_line + chunk_size - 1, total_lines)
|
||||||
|
# Get byte range for this chunk
|
||||||
|
start_byte = line_offsets[start_line - 1]
|
||||||
|
end_byte = line_offsets[end_line] - 1
|
||||||
|
|
||||||
|
byte_ranges.append((start_byte, end_byte))
|
||||||
|
|
||||||
|
return byte_ranges
|
||||||
|
|
||||||
|
|
||||||
|
def detect_delimiter(sample: TextIO) -> str:
|
||||||
|
"""Detect the delimiter character used in a CSV file.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
sample : TextIO
|
||||||
|
A file-like object opened in text mode (e.g., from `open('file.csv')`).
|
||||||
|
Must be readable and at position 0.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
The detected delimiter character (e.g., ',', ';', '\\t').
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
csv.Error
|
||||||
|
If the file cannot be parsed as CSV or delimiter detection fails.
|
||||||
|
ValueError
|
||||||
|
If the file is empty or contains no detectable delimiter.
|
||||||
|
"""
|
||||||
|
sniffer = csv.Sniffer()
|
||||||
|
dialect = sniffer.sniff(sample.read())
|
||||||
|
sample.seek(0)
|
||||||
|
|
||||||
|
return dialect.delimiter
|
||||||
0
user-management/app/events/__init__.py
Normal file
0
user-management/app/events/__init__.py
Normal file
0
user-management/app/events/batch/__init__.py
Normal file
0
user-management/app/events/batch/__init__.py
Normal file
20
user-management/app/events/batch/csv_into_chunks.py
Normal file
20
user-management/app/events/batch/csv_into_chunks.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from aws_lambda_powertools.utilities.data_classes import (
|
||||||
|
EventBridgeEvent,
|
||||||
|
event_source,
|
||||||
|
)
|
||||||
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
|
|
||||||
|
from boto3clients import s3_client
|
||||||
|
from config import CHUNK_SIZE
|
||||||
|
from csv_utils import byte_ranges
|
||||||
|
|
||||||
|
transport_params = {'client': s3_client}
|
||||||
|
|
||||||
|
|
||||||
|
@event_source(data_class=EventBridgeEvent)
|
||||||
|
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||||
|
new_image = event.detail['new_image']
|
||||||
|
csvfile = new_image['s3uri']
|
||||||
|
pairs = byte_ranges(csvfile, CHUNK_SIZE, transport_params=transport_params)
|
||||||
|
|
||||||
|
return True
|
||||||
14
user-management/app/events/batch/excel_to_csv.py
Normal file
14
user-management/app/events/batch/excel_to_csv.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from aws_lambda_powertools.utilities.data_classes import (
|
||||||
|
EventBridgeEvent,
|
||||||
|
event_source,
|
||||||
|
)
|
||||||
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
|
|
||||||
|
from boto3clients import s3_client
|
||||||
|
|
||||||
|
transport_params = {'client': s3_client}
|
||||||
|
|
||||||
|
|
||||||
|
@event_source(data_class=EventBridgeEvent)
|
||||||
|
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||||
|
return True
|
||||||
49
user-management/app/events/batch/read_csv_chunk.py
Normal file
49
user-management/app/events/batch/read_csv_chunk.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import csv
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
from aws_lambda_powertools.utilities.data_classes import (
|
||||||
|
EventBridgeEvent,
|
||||||
|
event_source,
|
||||||
|
)
|
||||||
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
|
|
||||||
|
from boto3clients import s3_client
|
||||||
|
|
||||||
|
transport_params = {'client': s3_client}
|
||||||
|
|
||||||
|
|
||||||
|
@event_source(data_class=EventBridgeEvent)
|
||||||
|
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||||
|
new_image = event.detail['new_image']
|
||||||
|
csvfile = new_image['s3_uri']
|
||||||
|
|
||||||
|
data = _get_s3_object_range(
|
||||||
|
csvfile,
|
||||||
|
start_byte=new_image['start_byte'],
|
||||||
|
end_byte=new_image['end_byte'],
|
||||||
|
s3_client=s3_client,
|
||||||
|
)
|
||||||
|
reader = csv.reader(data)
|
||||||
|
|
||||||
|
for x in reader:
|
||||||
|
print(x)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _get_s3_object_range(
|
||||||
|
s3_uri: str,
|
||||||
|
*,
|
||||||
|
start_byte: int,
|
||||||
|
end_byte: int,
|
||||||
|
s3_client,
|
||||||
|
) -> StringIO:
|
||||||
|
bucket, key = s3_uri.replace('s3://', '').split('/', 1)
|
||||||
|
|
||||||
|
response = s3_client.get_object(
|
||||||
|
Bucket=bucket,
|
||||||
|
Key=key,
|
||||||
|
Range=f'bytes={start_byte}-{end_byte}',
|
||||||
|
)
|
||||||
|
|
||||||
|
return StringIO(response['Body'].read().decode('utf-8'))
|
||||||
45
user-management/app/events/email_receiving.py
Normal file
45
user-management/app/events/email_receiving.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import urllib.parse as urllib_parse
|
||||||
|
from email.utils import parseaddr
|
||||||
|
from typing import Any, Iterator
|
||||||
|
|
||||||
|
from aws_lambda_powertools import Logger
|
||||||
|
from aws_lambda_powertools.utilities.data_classes import SESEvent, event_source
|
||||||
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
|
|
||||||
|
logger = Logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@logger.inject_lambda_context
|
||||||
|
@event_source(data_class=SESEvent)
|
||||||
|
def lambda_handler(event: SESEvent, context: LambdaContext) -> dict:
|
||||||
|
ses = event.record.ses
|
||||||
|
to = urllib_parse.unquote(ses.receipt.recipients[0]).lower()
|
||||||
|
name, email_from = parseaddr(get_header_value(ses.mail.headers, 'from'))
|
||||||
|
subject = get_header_value(
|
||||||
|
ses.mail.headers,
|
||||||
|
'subject',
|
||||||
|
default='',
|
||||||
|
raise_on_missing=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
if email_from == 'sergio@somosbeta.com.br':
|
||||||
|
return {'disposition': 'CONTINUE'}
|
||||||
|
|
||||||
|
return {'disposition': 'STOP_RULE_SET'}
|
||||||
|
|
||||||
|
|
||||||
|
def get_header_value(
|
||||||
|
headers: Iterator,
|
||||||
|
header_name: str,
|
||||||
|
*,
|
||||||
|
default: Any = None,
|
||||||
|
raise_on_missing: bool = True,
|
||||||
|
) -> str:
|
||||||
|
for header in headers:
|
||||||
|
if header.name.lower() == header_name:
|
||||||
|
return header.value
|
||||||
|
|
||||||
|
if raise_on_missing:
|
||||||
|
raise ValueError(f'{header_name} not found.')
|
||||||
|
|
||||||
|
return default
|
||||||
56
user-management/cf.py
Normal file
56
user-management/cf.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# /// script
|
||||||
|
# dependencies = [
|
||||||
|
# "cloudflare"
|
||||||
|
# ]
|
||||||
|
# ///
|
||||||
|
|
||||||
|
from cloudflare import Cloudflare
|
||||||
|
|
||||||
|
CLOUDFLARE_ACCOUNT_ID = '5436b62470020c04b434ad31c3e4cf4e'
|
||||||
|
CLOUDFLARE_API_TOKEN = 'gFndkBJCzH4pRX7mKXokdWfw1xhm8-9FHfvLfhwa'
|
||||||
|
|
||||||
|
|
||||||
|
client = Cloudflare(api_token=CLOUDFLARE_API_TOKEN)
|
||||||
|
|
||||||
|
assistant = """
|
||||||
|
You are a data analysis assistant specialized in identifying Brazilian
|
||||||
|
personal data from CSV files.
|
||||||
|
|
||||||
|
These CSV files may or may not include headers.
|
||||||
|
|
||||||
|
Your task is to analyze the content and identify only three possible
|
||||||
|
data types: 'name', 'cpf', and 'email'.
|
||||||
|
|
||||||
|
Ignore all other fields.
|
||||||
|
"""
|
||||||
|
|
||||||
|
csv_content = """
|
||||||
|
Sérgio Rafael de Siqueira,10,07879819908,osergiosiqueria@gmail.com,cipa
|
||||||
|
Tiago Maciel,12,086.790.049-01,tiago@somosbeta.com.br,nr 10
|
||||||
|
"""
|
||||||
|
|
||||||
|
prompt = f"""
|
||||||
|
Here is a CSV sample:
|
||||||
|
|
||||||
|
{csv_content}
|
||||||
|
|
||||||
|
Your task is to:
|
||||||
|
- Detect which columns most likely contain "name", "cpf", or "email".
|
||||||
|
- Skip any category that is not present in the data.
|
||||||
|
- Return ONLY a valid Python list of tuples, like:
|
||||||
|
[('name', index), ('cpf', index), ('email', index)]
|
||||||
|
- Use the column index that most likely matches each data type,
|
||||||
|
based on frequency and data format.
|
||||||
|
- Don't include explanations, code, or any additional text.
|
||||||
|
"""
|
||||||
|
|
||||||
|
r = client.ai.run(
|
||||||
|
model_name='@cf/meta/llama-3-8b-instruct',
|
||||||
|
account_id=CLOUDFLARE_ACCOUNT_ID,
|
||||||
|
messages=[
|
||||||
|
{'role': 'system', 'content': assistant},
|
||||||
|
{'role': 'user', 'content': prompt},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
print(r)
|
||||||
31
user-management/pyproject.toml
Normal file
31
user-management/pyproject.toml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
[project]
|
||||||
|
name = "user-management"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = ""
|
||||||
|
readme = ""
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dependencies = ["layercake"]
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"pytest>=8.3.4",
|
||||||
|
"pytest-cov>=6.0.0",
|
||||||
|
"ruff>=0.9.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
pythonpath = ["app/"]
|
||||||
|
addopts = "--cov --cov-report html -v"
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py311"
|
||||||
|
src = ["app"]
|
||||||
|
|
||||||
|
[tool.ruff.format]
|
||||||
|
quote-style = "single"
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["E", "F", "I"]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
layercake = { path = "../layercake" }
|
||||||
3
user-management/pyrightconfig.json
Normal file
3
user-management/pyrightconfig.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extraPaths": ["app/"]
|
||||||
|
}
|
||||||
9
user-management/samconfig.toml
Normal file
9
user-management/samconfig.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version = 0.1
|
||||||
|
[default.deploy.parameters]
|
||||||
|
stack_name = "saladeaula-user-management"
|
||||||
|
resolve_s3 = true
|
||||||
|
s3_prefix = "user_management"
|
||||||
|
region = "sa-east-1"
|
||||||
|
confirm_changeset = false
|
||||||
|
capabilities = "CAPABILITY_IAM"
|
||||||
|
image_repositories = []
|
||||||
110
user-management/template.yaml
Normal file
110
user-management/template.yaml
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
AWSTemplateFormatVersion: 2010-09-09
|
||||||
|
Transform: AWS::Serverless-2016-10-31
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
BucketName:
|
||||||
|
Type: String
|
||||||
|
Default: saladeaula.digital
|
||||||
|
UserTable:
|
||||||
|
Type: String
|
||||||
|
Default: betaeducacao-prod-users_d2o3r5gmm4it7j
|
||||||
|
|
||||||
|
Globals:
|
||||||
|
Function:
|
||||||
|
CodeUri: app/
|
||||||
|
Runtime: python3.13
|
||||||
|
Tracing: Active
|
||||||
|
Architectures:
|
||||||
|
- x86_64
|
||||||
|
Layers:
|
||||||
|
- !Sub arn:aws:lambda:sa-east-1:336641857101:layer:layercake:72
|
||||||
|
Environment:
|
||||||
|
Variables:
|
||||||
|
TZ: America/Sao_Paulo
|
||||||
|
LOG_LEVEL: DEBUG
|
||||||
|
POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1
|
||||||
|
POWERTOOLS_LOGGER_LOG_EVENT: true
|
||||||
|
USER_TABLE: !Ref UserTable
|
||||||
|
|
||||||
|
Resources:
|
||||||
|
EventLog:
|
||||||
|
Type: AWS::Logs::LogGroup
|
||||||
|
Properties:
|
||||||
|
RetentionInDays: 90
|
||||||
|
|
||||||
|
EventCsvChunksFunction:
|
||||||
|
Type: AWS::Serverless::Function
|
||||||
|
Properties:
|
||||||
|
Handler: events.batch.csv_chunks.lambda_handler
|
||||||
|
LoggingConfig:
|
||||||
|
LogGroup: !Ref EventLog
|
||||||
|
Policies:
|
||||||
|
- S3CrudPolicy:
|
||||||
|
BucketName: !Ref BucketName
|
||||||
|
Events:
|
||||||
|
DynamoDBEvent:
|
||||||
|
Type: EventBridgeRule
|
||||||
|
Properties:
|
||||||
|
Pattern:
|
||||||
|
resources: [betaeducacao-prod-users_d2o3r5gmm4it7j]
|
||||||
|
detail:
|
||||||
|
new_image:
|
||||||
|
sk:
|
||||||
|
- prefix: batch_jobs#
|
||||||
|
|
||||||
|
EventEmailReceivingFunction:
|
||||||
|
Type: AWS::Serverless::Function
|
||||||
|
Properties:
|
||||||
|
Handler: events.email_receiving.lambda_handler
|
||||||
|
LoggingConfig:
|
||||||
|
LogGroup: !Ref EventLog
|
||||||
|
|
||||||
|
LambdaInvokePermission:
|
||||||
|
Type: AWS::Lambda::Permission
|
||||||
|
Properties:
|
||||||
|
FunctionName: !GetAtt EventEmailReceivingFunction.Arn
|
||||||
|
Action: lambda:InvokeFunction
|
||||||
|
Principal: ses.amazonaws.com
|
||||||
|
SourceArn: !Sub arn:aws:ses:${AWS::Region}:${AWS::AccountId}:receipt-rule-set/*
|
||||||
|
|
||||||
|
BucketPolicy:
|
||||||
|
Type: AWS::S3::BucketPolicy
|
||||||
|
Properties:
|
||||||
|
Bucket: !Ref BucketName
|
||||||
|
PolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service: ses.amazonaws.com
|
||||||
|
Action: s3:PutObject
|
||||||
|
Resource: !Sub arn:aws:s3:::${BucketName}/*
|
||||||
|
Condition:
|
||||||
|
StringEquals:
|
||||||
|
aws:SourceAccount: !Ref AWS::AccountId
|
||||||
|
StringLike:
|
||||||
|
aws:SourceArn: !Sub arn:aws:ses:${AWS::Region}:${AWS::AccountId}:receipt-rule-set/*
|
||||||
|
|
||||||
|
EmailReceiptRuleSet:
|
||||||
|
Type: AWS::SES::ReceiptRuleSet
|
||||||
|
Properties:
|
||||||
|
RuleSetName: users.noreply.saladeaula.digital
|
||||||
|
|
||||||
|
EmailReceiptRule:
|
||||||
|
Type: AWS::SES::ReceiptRule
|
||||||
|
DependsOn:
|
||||||
|
- LambdaInvokePermission
|
||||||
|
- BucketPolicy
|
||||||
|
Properties:
|
||||||
|
RuleSetName: !Ref EmailReceiptRuleSet
|
||||||
|
Rule:
|
||||||
|
Name: lambda
|
||||||
|
Enabled: true
|
||||||
|
Actions:
|
||||||
|
- LambdaAction:
|
||||||
|
FunctionArn: !GetAtt EventEmailReceivingFunction.Arn
|
||||||
|
InvocationType: RequestResponse
|
||||||
|
- S3Action:
|
||||||
|
BucketName: !Ref BucketName
|
||||||
|
ObjectKeyPrefix: "mailbox"
|
||||||
|
ScanEnabled: true
|
||||||
0
user-management/tests/__init__.py
Normal file
0
user-management/tests/__init__.py
Normal file
16
user-management/tests/conftest.py
Normal file
16
user-management/tests/conftest.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LambdaContext:
|
||||||
|
function_name: str = 'test'
|
||||||
|
memory_limit_in_mb: int = 128
|
||||||
|
invoked_function_arn: str = 'arn:aws:lambda:eu-west-1:809313241:function:test'
|
||||||
|
aws_request_id: str = '52fdfc07-2182-154f-163f-5f0f9a621d72'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def lambda_context() -> LambdaContext:
|
||||||
|
return LambdaContext()
|
||||||
0
user-management/tests/events/__init__.py
Normal file
0
user-management/tests/events/__init__.py
Normal file
13
user-management/tests/events/batch/test_csv_into_chunks.py
Normal file
13
user-management/tests/events/batch/test_csv_into_chunks.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import events.batch.csv_into_chunks as app
|
||||||
|
|
||||||
|
|
||||||
|
def test_chunk_csv(lambda_context):
|
||||||
|
event = {
|
||||||
|
'detail': {
|
||||||
|
'new_image': {
|
||||||
|
's3uri': 's3://saladeaula.digital/samples/large_users.csv',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
app.lambda_handler(event, lambda_context) # type: ignore
|
||||||
136
user-management/tests/events/test_email_receiving.py
Normal file
136
user-management/tests/events/test_email_receiving.py
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
|
|
||||||
|
import events.email_receiving as app
|
||||||
|
|
||||||
|
event = {
|
||||||
|
'Records': [
|
||||||
|
{
|
||||||
|
'eventSource': 'aws:ses',
|
||||||
|
'eventVersion': '1.0',
|
||||||
|
'ses': {
|
||||||
|
'mail': {
|
||||||
|
'timestamp': '2025-05-29T15:50:41.604Z',
|
||||||
|
'source': 'sergio@somosbeta.com.br',
|
||||||
|
'messageId': '2994higq3tr7efijr3lj65etntffapgg1q7hea81',
|
||||||
|
'destination': [
|
||||||
|
'org+35980592000130@users.noreply.saladeaula.digital'
|
||||||
|
],
|
||||||
|
'headersTruncated': False,
|
||||||
|
'headers': [
|
||||||
|
{'name': 'Return-Path', 'value': '<sergio@somosbeta.com.br>'},
|
||||||
|
{
|
||||||
|
'name': 'Received',
|
||||||
|
'value': 'from mail-lf1-f54.google.com (mail-lf1-f54.google.com [209.85.167.54]) by inbound-smtp.sa-east-1.amazonaws.com with SMTP id 2994higq3tr7efijr3lj65etntffapgg1q7hea81 for org+35980592000130@users.noreply.saladeaula.digital; Thu, 29 May 2025 15:50:41 +0000 (UTC)',
|
||||||
|
},
|
||||||
|
{'name': 'X-SES-Spam-Verdict', 'value': 'PASS'},
|
||||||
|
{'name': 'X-SES-Virus-Verdict', 'value': 'PASS'},
|
||||||
|
{
|
||||||
|
'name': 'Received-SPF',
|
||||||
|
'value': 'pass (spfCheck: domain of somosbeta.com.br designates 209.85.167.54 as permitted sender) client-ip=209.85.167.54; envelope-from=sergio@somosbeta.com.br; helo=mail-lf1-f54.google.com;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'Authentication-Results',
|
||||||
|
'value': 'amazonses.com; spf=pass (spfCheck: domain of somosbeta.com.br designates 209.85.167.54 as permitted sender) client-ip=209.85.167.54; envelope-from=sergio@somosbeta.com.br; helo=mail-lf1-f54.google.com; dkim=pass header.i=@somosbeta.com.br; dmarc=none header.from=somosbeta.com.br;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'X-SES-RECEIPT',
|
||||||
|
'value': 'AEFBQUFBQUFBQUFHVWpuODdPY2tGUlordE5YWkVEUlZNWWZFYkpDMU5MUURyaHNVSldnTGhEVWhCQzd5UGpzWHI4LzJoS1VaN0lOU0FkMzJFU0h6MjVuUzk2c09KUXlzbUJQdHd6T0d0Y2ptZXhRVk1KY3RkOXpRamZMb3hwSGJIVlFla2tBcmZvRmYwQS9WU3hBVlBqcUpDYm00eTdiRnRqNW45ek9ld0ZyTGJKV3k2TXRpc0J6aGhBdmFvZDFDQ000Zm9QTng3VHljNXArM0hjT2ZsYkhtM3RCZnpRV1NOczU2RDdmL0RKclJOcDNvY2ZxV1hmajNYMkczVHpsWEZCMm40Z2pQM29udkMyb01vN3JwU0p2TUI1WGorN2JPd2RPYW5lUDN3T3RMRlhsdEpGbGNCa3c9PQ==',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'X-SES-DKIM-SIGNATURE',
|
||||||
|
'value': 'a=rsa-sha256; q=dns/txt; b=KPtFiBwsOTBl1YVLRTSfaZ+X6h7uSSOu/i1Cw6Pd+wvMBHRWy9EYcWUjyDjsLG/uYHShLW4+LHsSg9HiqrAP2jVJSAawrIwZr1wPQo7ovQvWuZfHQN/StgXIgBU+L7Bp6GSR26LRufxjj7q9YBmEeirjJ3d0G8E/rF2QqeITlpo=; c=relaxed/simple; s=bm3ypaoivbtdzmy3b6w37fzb5voa2uru; d=amazonses.com; t=1748533842; v=1; bh=kTUCV1DQAazu4FsUi1MrelD2QvSfHGArZ/c6A79t3/E=; h=From:To:Cc:Bcc:Subject:Date:Message-ID:MIME-Version:Content-Type:X-SES-RECEIPT;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'Received',
|
||||||
|
'value': 'by mail-lf1-f54.google.com with SMTP id 2adb3069b0e04-54b0d638e86so1570269e87.1 for <org+35980592000130@users.noreply.saladeaula.digital>; Thu, 29 May 2025 08:50:40 -0700 (PDT)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'DKIM-Signature',
|
||||||
|
'value': 'v=1; a=rsa-sha256; c=relaxed/relaxed; d=somosbeta.com.br; s=google; t=1748533838; x=1749138638; darn=users.noreply.saladeaula.digital; h=to:subject:message-id:date:from:in-reply-to:references:mime-version:from:to:cc:subject:date:message-id:reply-to; bh=kTUCV1DQAazu4FsUi1MrelD2QvSfHGArZ/c6A79t3/E=; b=Qi8gk/kTpwXCLDM7FPS7ULTy+9gO/4WsGL9zY1xEDw0Rp38f4rVR8L95hIhwK2daA27mq3pv9TdrK3XKQQIuSvRVvaM0b/evkZD8QhaT9tCmL0eKEBB4czGB0OSS3Q4qP34GFWMmXIaxoKIo1td76JnXbto9ZQvjUTBr3GGlF3Lm/MPTaAHs1b3dalv2diTvyj1tzoeb4wGePKsqLh5LKGOxbbWsxPeHEJ8sLM4LyJjxoqSOO0wgKdH5S/ZNpHWcJtXBntjiDUZNeQ5ucEn8ZLbADCObZZV/gH9i/cB1BmlSvJP3D07uJTAEBqyepd+W9fIW2mox/+fmOb3OEHRthQ==',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'X-Google-DKIM-Signature',
|
||||||
|
'value': 'v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1748533838; x=1749138638; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=kTUCV1DQAazu4FsUi1MrelD2QvSfHGArZ/c6A79t3/E=; b=UnL/uRXRahnH5uStZ276LH4kqpHigngw4iql9GHKmFaIKxJ8hLGn/wu7ie4ljnw8m/ I4CvhDKH4TVtIWPS81fm06PMgeqQYRX3jLhHvIltROCNVX6ZFzbAXgiAlk0NS1npvDYJ evVgSaPPco4D/8pMWZX4fUjU+8me32ChKxHsklEAts/LiD+MvTuHCHZovSEj1aXAz91b yGZe1bx2+phuqzUZyIOeheKjl4TNjEBx83omOzf9HtClKhjzCwHjfZ8uk2lhJ10ogKZ0 GNQ5OlnPkgdAg0/+HsifvGR6xfkFsiunIDyinBWoOhMU1o0+DiicxOIjY8QEayF3MLUt REoA==',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'X-Gm-Message-State',
|
||||||
|
'value': 'AOJu0Yxw0icQkFV090vn5hx/hKp0ePH78Wr0iqi4V3x4mpVXrRX8te2o 30aBYeZRPwn8SRSrq/kbn4bLcs5mPDB+iRP9IGFxS7KLSQi+KG4PQeDHyW3R/AgOPHACUUXUUyz Vcwny029WGY5PVhxlikAYdDfhNdO8GM2DKMV1+Oxy/a+qmt5LZeuy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'X-Gm-Gg',
|
||||||
|
'value': 'ASbGncslCMPPU/pax0+RNy/cQR/Y/wUroSJMvI2DCCMq6Qld+Ih1jG4+HnhQPqn3nTK EEW6/99tqazq+SKy+31AB77ajVczvJQTElRSW/+bhd42l7by2hicTKElcR3GWivlrqd1TywUZOB DkB9J/vupSV0PDCJfZVi+7Tb9Pb61nnxaU+SQ=',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'X-Google-Smtp-Source',
|
||||||
|
'value': 'AGHT+IFYi41KmJjGcfQmUvWJDdTAzGIv2JlL9XAwBpAb53mMOOm3tttzkhbvfuiKh/DI9NjITHuO3xuEPqnPI9lpum8=',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'X-Received',
|
||||||
|
'value': 'by 2002:a05:6512:1392:b0:553:2f61:58f1 with SMTP id 2adb3069b0e04-5532f615a8dmr2268707e87.53.1748533837647; Thu, 29 May 2025 08:50:37 -0700 (PDT)',
|
||||||
|
},
|
||||||
|
{'name': 'MIME-Version', 'value': '1.0'},
|
||||||
|
{
|
||||||
|
'name': 'References',
|
||||||
|
'value': '<CAMThe4mV9=1-BLiOi9MU3fAS=C6uYE9+3hKUjibrwxxngYNn2Q@mail.gmail.com>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'In-Reply-To',
|
||||||
|
'value': '<CAMThe4mV9=1-BLiOi9MU3fAS=C6uYE9+3hKUjibrwxxngYNn2Q@mail.gmail.com>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'From',
|
||||||
|
'value': 'Sérgio Rafael Siqueira <sergio@somosbeta.com.br>',
|
||||||
|
},
|
||||||
|
{'name': 'Date', 'value': 'Thu, 29 May 2025 12:50:26 -0300'},
|
||||||
|
{
|
||||||
|
'name': 'X-Gm-Features',
|
||||||
|
'value': 'AX0GCFvofROqzf21KTgiIJq_AULCNljEuNFUJBk2xQGwVKmPjim_4slYIOP0WRw',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'Message-ID',
|
||||||
|
'value': '<CAMThe4=yMRJg4YOcACYAR509N1RyWyQgAghyVmr=NuSJnbondg@mail.gmail.com>',
|
||||||
|
},
|
||||||
|
{'name': 'Subject', 'value': 'Re: test'},
|
||||||
|
{
|
||||||
|
'name': 'To',
|
||||||
|
'value': 'org+35980592000130@users.noreply.saladeaula.digital',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'Content-Type',
|
||||||
|
'value': 'multipart/alternative; boundary="00000000000045b8c206364842b3"',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'commonHeaders': {
|
||||||
|
'returnPath': 'sergio@somosbeta.com.br',
|
||||||
|
'from': ['"Sérgio Rafael Siqueira" <sergio@somosbeta.com.br>'],
|
||||||
|
'date': 'Thu, 29 May 2025 12:50:26 -0300',
|
||||||
|
'to': ['org+35980592000130@users.noreply.saladeaula.digital'],
|
||||||
|
'messageId': '<CAMThe4=yMRJg4YOcACYAR509N1RyWyQgAghyVmr=NuSJnbondg@mail.gmail.com>',
|
||||||
|
'subject': 'Re: test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'receipt': {
|
||||||
|
'timestamp': '2025-05-29T15:50:41.604Z',
|
||||||
|
'processingTimeMillis': 1105,
|
||||||
|
'recipients': [
|
||||||
|
'org+35980592000130@users.noreply.saladeaula.digital'
|
||||||
|
],
|
||||||
|
'spamVerdict': {'status': 'PASS'},
|
||||||
|
'virusVerdict': {'status': 'PASS'},
|
||||||
|
'spfVerdict': {'status': 'PASS'},
|
||||||
|
'dkimVerdict': {'status': 'PASS'},
|
||||||
|
'dmarcVerdict': {'status': 'GRAY'},
|
||||||
|
'action': {
|
||||||
|
'type': 'Lambda',
|
||||||
|
'functionArn': 'arn:aws:lambda:sa-east-1:336641857101:function:saladeaula-user-managemen-EventEmailReceivingFunct-LmnnEfi9tL2O',
|
||||||
|
'invocationType': 'Event',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_email_receiving(lambda_context: LambdaContext):
|
||||||
|
assert app.lambda_handler(event, lambda_context) == {'disposition': 'CONTINUE'}
|
||||||
3286
user-management/tests/samples/large_users.csv
Normal file
3286
user-management/tests/samples/large_users.csv
Normal file
File diff suppressed because it is too large
Load Diff
28
user-management/tests/samples/users.csv
Normal file
28
user-management/tests/samples/users.csv
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
CADASTRO DE COLABORADOR,,,,
|
||||||
|
,NOME COMPLETO,EMAIL (letra minúscula),CPF,TREINAMENTO
|
||||||
|
,ANDRE HENRIQUE LOPES ZAFALON,henrique.zafalon@fanucamerica.com,261.955.138-22,NR-35 (RECICLAGEM)
|
||||||
|
,SERGIO DA SILVA CUPERTINO,sergio.cupertino@fanucamerica.com,066.945.708-64,NR-10 (RECICLAGEM)
|
||||||
|
,SERGIO DA SILVA CUPERTINO,sergio.cupertino@fanucamerica.com,066.945.708-64,NR-35 (RECICLAGEM)
|
||||||
|
,ROVANE CAMPOS,rovane.campos@fanucamerica.com,095.958.578-82,NR-10 (RECICLAGEM)
|
||||||
|
,ROVANE CAMPOS,rovane.campos@fanucamerica.com,095.958.578-82,NR-35 (RECICLAGEM)
|
||||||
|
,MARCIO ATSUSHI KANEKO MASUDA,marcio.masuda@fanucamerica.com,293.042.798-10,NR-10 (RECICLAGEM)
|
||||||
|
,FABIO AKIRA HARAGUCHI,fabio.haraguchi@fanucamerica.com,287.018.428-03,NR-10 (RECICLAGEM)
|
||||||
|
,EMIDIO YOITI MOCHIZUKI,emidio.mochizuki@fanucamerica.com,268.579.208-26,NR-10 (RECICLAGEM)
|
||||||
|
,EMIDIO YOITI MOCHIZUKI,emidio.mochizuki@fanucamerica.com,268.579.208-26,NR-35 (RECICLAGEM)
|
||||||
|
,ERIC HIDEKI MORIKIO,eric.morikio@fanucamerica.com,417.359.838-61,NR-10 (RECICLAGEM)
|
||||||
|
,HENRIQUE DE FIGUEIREDO BASTOS FERRAZ,henrique.ferraz@fanucamerica.com,417.059.788-51,NR-10 (RECICLAGEM)
|
||||||
|
,LAYS MORETTI DA SILVA,lays.silva@fanucamerica.com,013.107.662-07,NR-10 (RECICLAGEM)
|
||||||
|
,LAYS MORETTI DA SILVA,lays.silva@fanucamerica.com,013.107.662-07,NR-12
|
||||||
|
,ANDRE DE SOUZA,andre.souza@fanucamerica.com,290.688.648-31,NR-10 (RECICLAGEM)
|
||||||
|
,ANDRE DE SOUZA,andre.souza@fanucamerica.com,290.688.648-31,NR-12
|
||||||
|
,RAFAEL TOSHIO BURATO MAEDA,rafael.maeda@fanucamerica.com,394.153.268-59,NR-10 (RECICLAGEM)
|
||||||
|
,RAFAEL TOSHIO BURATO MAEDA,rafael.maeda@fanucamerica.com,394.153.268-59,NR-12
|
||||||
|
,RAFAEL TOSHIO BURATO MAEDA,rafael.maeda@fanucamerica.com,394.153.268-59,NR-35 (RECICLAGEM)
|
||||||
|
,RICARDO GALLES BONET,ricardo.bonet@fanucamerica.com,424.430.528-93,NR-10 (RECICLAGEM)
|
||||||
|
,RULIO SIEFERT SERA,rulio.sera@fanucamerica.com,063.916.859-08,NR-10 (RECICLAGEM)
|
||||||
|
,MACIEL FERREIRA BOMFIM,maciel.bomfim@fanucamerica.com,334.547.088-85,NR-10 (RECICLAGEM)
|
||||||
|
,JAIME EDUARDO GALVEZ AVILES,jaime.galvez@fanucamerica.com,280.238.818-50,NR-12
|
||||||
|
,JAIME EDUARDO GALVEZ AVILES,jaime.galvez@fanucamerica.com,280.238.818-50,NR-35 (RECICLAGEM)
|
||||||
|
,HIGOR MACHADO SILVA,higor.silva@fanucamerica.com,419.879.878-88,NR-12
|
||||||
|
,LÁZARO SOUZA DIAS,lazaro.dias@fanucamerica.com,067.179.825-19,NR-12
|
||||||
|
,JOÃO PEDRO AGUIAR GALASSO,joao.pedro@fanucamerica.com,570.403.588-40,NR-12
|
||||||
|
29
user-management/tests/test_csv_utils.py
Normal file
29
user-management/tests/test_csv_utils.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from csv_utils import byte_ranges, detect_delimiter
|
||||||
|
|
||||||
|
|
||||||
|
def test_detect_delimiter():
|
||||||
|
with open('tests/samples/users.csv') as fp:
|
||||||
|
assert detect_delimiter(fp) == ','
|
||||||
|
|
||||||
|
|
||||||
|
def test_byte_ranges():
|
||||||
|
csvpath = 'tests/samples/users.csv'
|
||||||
|
ranges = byte_ranges(csvpath, 10)
|
||||||
|
*_, pair = ranges
|
||||||
|
start_byte, end_byte = pair
|
||||||
|
|
||||||
|
assert ranges == [(0, 808), (809, 1655), (1656, 2303)]
|
||||||
|
|
||||||
|
expected = """,RICARDO GALLES BONET,ricardo.bonet@fanucamerica.com,424.430.528-93,NR-10 (RECICLAGEM)
|
||||||
|
,RULIO SIEFERT SERA,rulio.sera@fanucamerica.com,063.916.859-08,NR-10 (RECICLAGEM)
|
||||||
|
,MACIEL FERREIRA BOMFIM,maciel.bomfim@fanucamerica.com,334.547.088-85,NR-10 (RECICLAGEM)
|
||||||
|
,JAIME EDUARDO GALVEZ AVILES,jaime.galvez@fanucamerica.com,280.238.818-50,NR-12
|
||||||
|
,JAIME EDUARDO GALVEZ AVILES,jaime.galvez@fanucamerica.com,280.238.818-50,NR-35 (RECICLAGEM)
|
||||||
|
,HIGOR MACHADO SILVA,higor.silva@fanucamerica.com,419.879.878-88,NR-12
|
||||||
|
,LÁZARO SOUZA DIAS,lazaro.dias@fanucamerica.com,067.179.825-19,NR-12
|
||||||
|
,JOÃO PEDRO AGUIAR GALASSO,joao.pedro@fanucamerica.com,570.403.588-40,NR-12"""
|
||||||
|
|
||||||
|
with open(csvpath, 'rb') as f:
|
||||||
|
f.seek(start_byte)
|
||||||
|
data = f.read(end_byte - start_byte + 1)
|
||||||
|
assert data.decode('utf-8') == expected
|
||||||
1038
user-management/uv.lock
generated
Normal file
1038
user-management/uv.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user