Não consegui publicar esses dias a continuação do nosso projeto mas hoje vamos ver três soluções para o desafio do D23. Uma bem mais verbosa mas que achei interessante colocar aqui pois exercita nossa capacidade em desenhar algoritmos, uma bem mais alto nível sugerida pelo @loscinco que eu mesmo não lembrava que existia ou nunca tenha chegado a usar, e uma usando uma lib built-in do Python. Por isso é interessante soltar desafios aqui, pois o pessoal procura e eu também aprendo.
Enfim, o problema era transformar essa parte da nossa página inicial:

Numa formatação com pontos como separador de milhar:

A forma que os dados eram recebidos da API era um inteiro grande, e pra exibir dessa forma o segredo é exibir o dado como string.
Para isso, como falei, tem uma forma mais alto nível, menos verbosa, usando as ferramentas de formatação de strings do Python. Aqui entra a contribuição do @loscinco.
O próprio Python consegue usar separador de milhar, mas esse separador de milhar em inglês são vírgulas e não pontos:
In [1]: milhao = 1000000
In [2]: milhao_formatado = "{:,}".format(milhao)
In [3]: milhao_formatado
Out[3]: '1,000,000'
In [4]: # ou usando f strings
In [5]: milhao_formatado = f"{milhao:,}"
In [6]: milhao_formatado
Out[6]: '1,000,000'
O segredo tá nos dois pontos e a vírgula. Ali estou indicando para o Python que quero formatar essa string que é um número e separar os milhares. E como Python foi escrita em inglês, o separador é a vírgula. Se eu tentar usar o ponto o console vai gerar um erro:
In [8]: milhao_formatado = f"{milhao:.}"
--------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-8-1041c03eee53> in <module>
----> 1 milhao_formatado = f"{milhao:.}"
ValueError: Format specifier missing precision
Mas nosso site é em português e separador de milhar em português é o ponto. Então eu preciso fazer um replace das vírgulas e transformá-las em pontos:
In [9]: milhao_formatado
Out[9]: '1,000,000'
In [10]: milhao_formatado.replace(',', '.')
Out[10]: '1.000.000'
Essa é a primeira solução.
A segunda é usando a lib built-in do Python chamada locale:
In [18]: import locale
In [19]: locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8')
Out[19]: 'pt_BR.UTF-8'
In [20]: milhao = 1000000
In [21]: f'{milhao:n}'
Out[21]: '1.000.000'
Acima eu importo a lib, que é uma lib de internacionalização, configuro a língua para português do Brasil no formato de codificação UTF-8.
Em seguida defino a variável com um número inteiro grande e na hora de printar o valor novamente eu vou usar os dois pontos mas seguidos de um "n".
O resultado é o mesmo, o número formatado com os pontos.
Agora vamos pra solução mais verbosa, que pode ser útil para gente treinar algoritmos, mas na realidade não é muito prática.
Vamos criar um dicionário de exemplo supondo os valores que mostraremos no nosso site:
In [1]: dic = {"confirmados": "2500000", "recuperados": "600000", "mortes": "165000"}
Os números estão em formato de string pois assim é como a API nos envia os dados. Se ela enviasse em formato inteiro, bastava transformar em string usando a função "str".
A primeira ideia que eu tive foi de iterar por cada valor desse dicionário e transformar cada valor em uma lista de caracteres, assim:
In [3]: for key, value in dic.items():
...: print(list(value))
...:
['2', '5', '0', '0', '0', '0', '0']
['6', '0', '0', '0', '0', '0']
['1', '6', '5', '0', '0', '0']
Em seguida, o que pensei foi em inverter os elementos de cada lista:
In [7]: for key, value in dic.items():
...: number_list = list(value)
...: number_list.reverse()
...: print(number_list)
...:
...:
['0', '0', '0', '0', '0', '5', '2']
['0', '0', '0', '0', '0', '6']
['0', '0', '0', '5', '6', '1']
E por que inverter os números na lista?
Porque minha ideia depois foi iterar sobre cada lista dessa e quando 3 itens tivessem passado, inserir um ponto na próxima posição da lista. Para isso eu decidi usar a função enumerate:
In [8]: for i, j in enumerate(['a', 'b', 'c', 'd']):
...: print(i, j)
...:
0 a
1 b
2 c
3 d
O enumerate é útil quando precisamos iterar sobre um iterável e saber os índices de cada elemento para usá-los de alguma forma.
Então a lógica agora é essa, iterar sobre cada lista de números e quando o índice obedecer à lógica de inserção de pontos, inserir esses pontos.
Pensei em dividir o valor do índice por 4 e se o resto da divisão for 3, inserir o ponto.
Pensando num número como o 1000:
In [11]: ex = list('1000')
In [12]: ex
Out[12]: ['1', '0', '0', '0']
Invertendo a lista:
In [13]: ex.reverse()
In [14]: ex
Out[14]: ['0', '0', '0', '1']
O índice 0 representa o valor 0, o índice 1, o valor 0 também, o índice 2, valor 0 e o índice 3 o valor 1.
A inserção vai ocorrer no índice 3, empurrando o "1" pra frente, então o índice é o 3 e se dividir 3 por 4 o resto é 3. Se dividir 7 por 4 o resto também é 3. Então sempre nessa lógica serão incluídos pontos na posição correta.
O algoritmo vai ficar mais ou menos assim:
In [10]: for key, value in dic.items():
...: number_list = list(value)
...: number_list.reverse()
...: for index, char in enumerate(number_list):
...: if index % 4 == 3:
...: number_list.insert(index, '.')
...: number_list.reverse()
...: print(number_list)
...:
['2', '.', '5', '0', '0', '.', '0', '0', '0']
['6', '0', '0', '.', '0', '0', '0']
['1', '6', '5', '.', '0', '0', '0']
Vejam que cada lista agora tem o ponto na posição correta.
E como eu transformo essas listas em strings de novo? Usando a função join:
In [15]: for key, value in dic.items():
...: number_list = list(value)
...: number_list.reverse()
...: for index, char in enumerate(number_list):
...: if index % 4 == 3:
...: number_list.insert(index, '.')
...: number_list.reverse()
...: print(number_list)
...: print("".join(number_list))
...:
['2', '.', '5', '0', '0', '.', '0', '0', '0']
2.500.000
['6', '0', '0', '.', '0', '0', '0']
600.000
['1', '6', '5', '.', '0', '0', '0']
165.000
Essa seria uma outra ideia para transformar os valores em strings com o separador de milhar.
Então o algoritmo ficaria mais ou menos assim:
for key, value in world_data.items():
data_str_list = list(value)
data_str_list.reverse()
for index, char in enumerate(data_str_list):
if index % 4 == 3:
data_str_list.insert(index, ".")
data_str_list.reverse()
world_data[key] = "".join(data_str_list)
Sim, eu sei, nada prático. Mas é um bom treino pra exercitar a mente, isso sem dúvida.
Existe uma quarta solução mas é pra quem usa o framework Django e é a que eu costumo usar.
Basta puxar uma extensão, um app, do Django para as configurações e dentro do template usar essa extensão. É bem simples mas foge do escopo do projeto já que o projeto é em Flask. E com certeza deve ter uma solução parecida em Flask mas a ideia do desafio era usar Python puro e foi o que fizemos.
A ideia do tópico de hoje era começar a publicar a parte dos gráficos do projeto mas essa parte dá um certo trabalho e não quero iniciar isso como nota de rodapé de outro tópico. Então veremos isso no próximo tópico. Se der tempo eu posto hoje mesmo pra compensar os dias que não postei.