Switching the crystal functions to faaso
This commit is contained in:
11
historico/README.md
Normal file
11
historico/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Readme for Historico
|
||||
|
||||
This is a funko using the Crystal runtime for [FaaSO](https://git.ralsina.me/ralsina/faaso)
|
||||
|
||||
## What is Historico
|
||||
|
||||
Write here what it is
|
||||
|
||||
## How to use Historico
|
||||
|
||||
And so on.
|
66
historico/funko.cr
Normal file
66
historico/funko.cr
Normal file
@ -0,0 +1,66 @@
|
||||
require "json"
|
||||
require "kemal"
|
||||
require "pg"
|
||||
require "pool/connection"
|
||||
|
||||
# get credentials from secrets
|
||||
USER = File.read("/secrets/user").strip
|
||||
PASS = File.read("/secrets/pass").strip
|
||||
DBHOST = File.read("/secrets/dbhost").strip
|
||||
|
||||
DBURL = "postgres://#{USER}:#{PASS}@#{DBHOST}:5432/nombres"
|
||||
puts "Connnecting to #{DBURL}"
|
||||
|
||||
# Create a connection pool to the database
|
||||
pg = ConnectionPool.new(capacity: 5, timeout: 0.01.seconds) do
|
||||
PG.connect(DBURL)
|
||||
end
|
||||
|
||||
# Connect to the database and get information about
|
||||
# the requested names
|
||||
get "/" do |env|
|
||||
# Names are query parameters
|
||||
# Split by commas, capitalize and take the first 5
|
||||
names = env.params.query["names"]
|
||||
.split(",").map(&.strip.capitalize)[..4]
|
||||
|
||||
# Prepare results table
|
||||
results = [] of Array(String)
|
||||
results << ["Año"] + names
|
||||
(1922..2015).each do |anio|
|
||||
results << [anio.to_s]
|
||||
end
|
||||
# Connect using credentials provided
|
||||
pg.connection do |cursor|
|
||||
# Get the information for each name
|
||||
names.map do |name|
|
||||
# Normalize: remove diacritics etc.
|
||||
name = name.unicode_normalize(:nfkd)
|
||||
.chars.reject! { |character|
|
||||
!character.ascii_letter? && (character != ' ')
|
||||
}.join("").downcase
|
||||
|
||||
counter_per_year = {} of Int32 => Int32
|
||||
cursor.query("
|
||||
SELECT anio::integer, contador::integer
|
||||
FROM nombres WHERE nombre = $1", name) do |result_set|
|
||||
result_set.each do
|
||||
counter_per_year[result_set.read(Int32)] = result_set.read(Int32)
|
||||
end
|
||||
end
|
||||
(1922..2015).each do |anio|
|
||||
results[anio - 1921] << counter_per_year.fetch(anio, 0).to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
results.to_json
|
||||
end
|
||||
|
||||
# The `/ping/` endpoint is configured in the container as a healthcheck
|
||||
# You can make it better by checking that your database is responding
|
||||
# or whatever checks you think are important
|
||||
#
|
||||
get "/ping/" do
|
||||
pg.connection.exec("SELECT 42")
|
||||
"OK"
|
||||
end
|
11
historico/funko.yml
Normal file
11
historico/funko.yml
Normal file
@ -0,0 +1,11 @@
|
||||
name: historico
|
||||
runtime: kemal
|
||||
options:
|
||||
shard_build_options: ""
|
||||
ship_packages: []
|
||||
devel_packages: []
|
||||
healthcheck_options: "--interval=1m --timeout=2s --start-period=2s --retries=3"
|
||||
healthcheck_command: "curl --fail http://localhost:3000/ping || exit 1"
|
||||
copy_from_build:
|
||||
- "public public"
|
||||
- "bin/funko ."
|
@ -1,66 +0,0 @@
|
||||
import unicodedata
|
||||
import urllib
|
||||
from collections import defaultdict as ddict
|
||||
from dataclasses import dataclass
|
||||
from json import loads
|
||||
|
||||
import pygal
|
||||
import pyrqlite.dbapi2 as dbapi2
|
||||
import requests
|
||||
|
||||
connection = dbapi2.connect(
|
||||
host="10.61.0.1",
|
||||
user="root",
|
||||
port=4001,
|
||||
password="",
|
||||
)
|
||||
|
||||
|
||||
def remove_accents(input_str):
|
||||
nfkd_form = unicodedata.normalize("NFKD", input_str)
|
||||
return "".join([c for c in nfkd_form if not unicodedata.combining(c)])
|
||||
|
||||
|
||||
def handle(req):
|
||||
"""handle a request to the function
|
||||
Args:
|
||||
req (str): request body
|
||||
|
||||
{"i": ["nombre1, nombre2"]}
|
||||
|
||||
"""
|
||||
nombres = []
|
||||
try:
|
||||
nombres = loads(req)
|
||||
nombres = nombres["i"].split(",")
|
||||
nombres = [remove_accents(x.strip().lower()) for x in nombres]
|
||||
nombres = [n for n in nombres if n]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not nombres:
|
||||
nombres = ["maria", "juan"]
|
||||
|
||||
chart = pygal.Line(
|
||||
height=200, fill=True, human_readable=True, show_minor_x_labels=False
|
||||
)
|
||||
chart.x_labels = [str(x) for x in range(1922, 2015)]
|
||||
chart.x_labels_major = [str(x) if x % 10 == 0 else "" for x in range(1922, 2015)]
|
||||
for nombre in nombres:
|
||||
datos = ddict(int)
|
||||
with connection.cursor() as cursor:
|
||||
sql = """
|
||||
SELECT anio, contador, nombre
|
||||
FROM nombres
|
||||
WHERE nombre = :nombre
|
||||
ORDER BY anio
|
||||
"""
|
||||
cursor.execute(sql, {"nombre": nombre})
|
||||
datos.update({r["anio"]: r["contador"] for r in cursor.fetchall()})
|
||||
chart.add(nombre.title(), [datos[x] for x in range(1922, 2015)])
|
||||
|
||||
chart.x_labels = [str(n) for n in range(1922, 2015)]
|
||||
chart.x_labels_major = [str(n) for n in range(1920, 2020, 10)]
|
||||
|
||||
# return Response(chart.render(is_unicode=True), mimetype="image/svg+xml")
|
||||
return chart.render(is_unicode=True), 200, {"Content-Type": "image/svg+xml"}
|
109
historico/public/index.html
Normal file
109
historico/public/index.html
Normal file
@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="https://www.gstatic.com/charts/loader.js"
|
||||
></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<!-- Font -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@300..700&display=swap"
|
||||
rel="stylesheet">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.colors.min.css"
|
||||
/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Kode+Mono&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script type="text/javascript">
|
||||
google.charts.load("current", { packages: ["corechart"] });
|
||||
google.charts.setOnLoadCallback(drawChart);
|
||||
|
||||
async function drawChart() {
|
||||
fetch(
|
||||
"/faaso/historico/?" +
|
||||
new URLSearchParams({
|
||||
names: document.getElementById("nombres").value,
|
||||
})
|
||||
)
|
||||
.then((response) => response.json())
|
||||
.then((json) => {
|
||||
var data = [json[0]];
|
||||
data.push(
|
||||
...json
|
||||
.slice(1)
|
||||
.map((item) => item.map((value) => parseInt(value)))
|
||||
);
|
||||
data = google.visualization.arrayToDataTable(data);
|
||||
var options = {
|
||||
title: "",
|
||||
animation: {
|
||||
startup: true,
|
||||
duration: 1000,
|
||||
easing: "out",
|
||||
},
|
||||
backgroundColor: "#1c212c",
|
||||
vAxis: {
|
||||
minValue: 0,
|
||||
gridlines: { color: "#666" },
|
||||
minorGridlines: { color: "#1c212c" },
|
||||
textStyle: { color: "#aaa" }
|
||||
},
|
||||
hAxis: {
|
||||
gridlines: { color: "#666" },
|
||||
minorGridlines: { color: "#1c212c" },
|
||||
textStyle: { color: "#aaa" }
|
||||
},
|
||||
legend: { position: "bottom", textStyle: { color: "#aaa" } },
|
||||
};
|
||||
|
||||
var chart = new google.visualization.LineChart(
|
||||
document.getElementById("chart")
|
||||
);
|
||||
|
||||
chart.draw(data, options);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
html * {
|
||||
font-family: 'Quicksand', sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container" style="text-align: center">
|
||||
<header>
|
||||
<h1>Popularidad de Nombres en Argentina</h1>
|
||||
</header>
|
||||
<div id="chart" style="width: 80vw; height: 50vh; margin: auto"></div>
|
||||
<form
|
||||
role="search"
|
||||
onSubmit="return false;"
|
||||
style="margin: auto; margin-top: 2em; width: 80%"
|
||||
>
|
||||
<input
|
||||
type="search"
|
||||
name="nombres"
|
||||
id="nombres"
|
||||
placeholder="Nombres separados con comas"
|
||||
aria-label="Search"
|
||||
/>
|
||||
<input type="submit" value="Buscar" onCLick="drawChart();" />
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
@ -1,3 +0,0 @@
|
||||
pygal
|
||||
requests
|
||||
pyrqlite
|
14
historico/shard.yml
Normal file
14
historico/shard.yml
Normal file
@ -0,0 +1,14 @@
|
||||
name: historico
|
||||
version: 0.1.0
|
||||
|
||||
targets:
|
||||
funko:
|
||||
main: main.cr
|
||||
|
||||
dependencies:
|
||||
kemal:
|
||||
github: kemalcr/kemal
|
||||
pg:
|
||||
github: will/crystal-pg
|
||||
pool:
|
||||
github: ysbaddaden/pool
|
Reference in New Issue
Block a user