Dúvida de ontem do @Luster aqui na área me inspirou a criar um tópico sobre o assunto, vai que é a dúvida de mais pessoas além dele (lembro que o @thiagofer70 já teve dúvida parecida) . A dúvida dele:

Então, basicamente a dúvida é entre separar o que é front-end de back-end e quando um interage com o outro.
Eu poderia ser bem abstrato aqui e explicar de forma simplificada usando a Bastter.com de exemplo, dizendo que essa tela que nós lemos os posts e comentamos é o front end e o back end é algum código que ninguém tá vendo o que faz mas que no fim das contas salva nossos comentários e tópicos num banco de dados, puxa comentários do banco de dados e renderiza os textos na tela, puxa os dados da empresa no banco de dados e renderiza os quadros simples e completos, etc.
Mas vou usar um exemplo diferente e mais simples do que a Bastter.com, que é um sisteminha web que já comentei aqui que criei para resolver problemas que eu tinha no trabalho, que está hospedado neste domínio:
AnalyTools . Aqui vou usar alguns exemplos de interação do back-end com o front-end pra deixar mais claro.
Obs: Abaixo vou listar alguns códigos meio grandes, não se preocupem em lê-los inteiros, é pra sentir só o big picture.
O site tem 2 ferramentas (uma terceira ainda não foi construída) que me ajudam no meu trabalho. Vou escolher uma pra dissecar essa diferença entre front end e back end, que vai ser esta:
AnalyToolsO link acima apresenta esta tela abaixo:

E isso é o front-end da aplicação. Nele há um botão para selecionar alguns arquivos (no caso do programa, arquivos com extensão .csv), um campo para escolher um nome para o arquivo que será gerado, outro botão para alterar parâmetros da análise (o "APLICAR FILTRO DE SAVIZTKY-GOLAY") e o botão azul é o de processamento dos arquivos. Aqui, como forma de simplificar a explicação, vou só selecionar alguns arquivos .csv, adicionar um nome e apertar o botão azul. No fim das contas será gerado um arquivo extensão .xlsx com todos os .csv compilados.
O código para gerar essa tela é o código responsável pelo front end, que são basicamente arquivos HTML, CSS e JavaScript, e eles podem ser vistos neste link:
analytics-tools/app at master · ThiagoDiasV/analytics-tools · GitHubA pasta "templates" armazena todos os arquivos HTML. A pasta "static" armazena os arquivos CSS e JavaScript. Vamos dar uma olhada no HTML que gera esta página:
{% extends 'base.html' %}
{% block content %}
{% with messages = get_flashed_messages() %}
{% if messages %}
<script>
var messages = {{ messages | safe }};
for (var i = 0; i < messages.length; i++) {
alert(messages[i]);
}
</script>
{% endif %}
{% endwith %}
<h2>SpectroWSM</h2>
<p>Selecione os arquivos com o botão abaixo:</p>
<form method="post" enctype="multipart/form-data" name="csvsForm" onsubmit="return validateCsvForm()">
{{ form.hidden_tag() }}
<p>
{{ form.csv_files(class="waves-effect waves-light btn teal lighten-1") }}
</p>
<div class="input-field">
<p>
{{ form.filename_field.label }}
{{ form.filename_field(class="validate") }}
</p>
</div>
<ul class="collapsible">
<li id="show-savgol-menu">
<div id="savgol-menu-trigger" class="collapsible-header teal lighten-1" onclick="sendValuesOfSavgolFilter()">Aplicar filtro de Saviztky-Golay</div>
<input id="savgol-option" name="savgol-option" type="hidden" value="0">
<div id="savgol-menu" class="collapsible-body">
<div class="input-field col s12">
<p>{{ form.windowlength.label }}</p>
{{ form.windowlength(class="browser-default windowlength") }}
</div>
<div class="input-field col s12">
<p>{{ form.polyorder.label }}</p>
{{ form.polyorder(class="browser-default polyorder") }}
</div>
<ul class="collapsible">
<li id="show-derivative-menu">
<div id="derivative-menu-trigger" class="collapsible-header teal lighten-1" onclick="sendValuesOfDerivative()">Calcular derivadas</div>
<input type="hidden" id="derivative-option" name="derivative-option" value="0">
<div id="derivative-menu" class="collapsible-body">
<p>{{ form.derivative.label }}</p>
{{ form.derivative(class="browser-default derivative") }}
<p>{{ form.deltalambda.label }}</p>
{{ form.deltalambda(class="browser-default deltalambda") }}
</div>
</li>
</ul>
</div>
</li>
</ul>
<p>
<button class="waves-effect waves-light btn blue lighten-1 upload-button" type="submit">
Processar arquivos
<i class="material-icons right">send</i>
</button>
</p>
</form>
{% endblock %}
O que, pra quem já estudou HTML, parece um HTML bem estranho, com todas essas chaves com porcentagens dentro, esse {% extends 'base.html' %}, tem até um {% if messages %} e etc e afins. Aqui já é uma demonstração de interação de back-end com front-end.
Este arquivo HTML não é um HTML puro, e sim um template. Templates são gerados por frameworks web, que são a parte do back end da aplicação. Essa aplicação de exemplo foi criada usando um framework de Python chamado Flask, que é o que estamos usando no nosso projeto atual. E frameworks existem para facilitar nossa vida.
Se vocês navegarem pelo site principal, verão que todas as páginas apresentam uma barra de navegação superior e uma barra de rodapé:


Aqui na Bastter.com também, tem certas coisas que são fixas e existem em todas as páginas, como a barra de navegação superior, a barra lateral, etc:

Imaginem que sacal seria copiar e colar o código HTML para isso em todos os arquivos HTML de todas as páginas, tanto do meu site como aqui na Bastter.
Essa tarefa sacal é uma das coisas boas que frameworks back-end fazem pra gente. No caso da minha aplicação, existe um HTML base, que é este:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='css/lightbox.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" type="text/css">
<link rel="shortcut icon" href="{{ url_for('static', filename='images/favicon.ico') }}">
<script src="https://kit.fontawesome.com/22d9277103.js" crossorigin="anonymous"></script>
<title>AnalyTools</title>
</head>
<body class="grey lighten-3">
<nav class="teal darken-4">
<div class="nav-wrapper container">
<a href="{{ url_for('index') }}" class="brand-logo">AnalyTools</a>
<a href="#" data-target="mobile-demo" class="sidenav-trigger"><i class="material-icons">menu</i></a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li><a href="{{ url_for('hplcpc') }}">HplcPC</a></li>
<li><a href="{{ url_for('spectrowsm') }}">SpectroWSM</a></li>
<li><a href="{{ url_for('viscorm') }}">ViscoRM</a></li>
</ul>
</div>
</nav>
<ul class="sidenav" id="mobile-demo">
<li><a href="{{ url_for('hplcpc') }}">HplcPC</a></li>
<li><a href="{{ url_for('spectrowsm') }}">SpectroWSM</a></li>
<li><a href="{{ url_for('viscorm') }}">ViscoRM</a></li>
</ul>
<div class="container">
{% block content %}
{% endblock %}
</div>
<footer class="page-footer brown darken-4">
<div class="footer-copyright container">
<div class="footer-text container">Desenvolvido por ThiagoDiasV
<a href="https://github.com/ThiagoDiasV" class="waves-effect waves-light btn-floating social black github right">
<i class="fab fa-github"></i></a>
</div>
</div>
</footer>
</body>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script src="{{ url_for('static', filename='js/lightbox.js') }}"></script>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/hplcpc.js') }}"></script>
<script src="{{ url_for('static', filename='js/spectrowsm.js') }}"></script>
<script src="{{ url_for('static', filename='js/index.js') }}"></script>
</html>
E todas as outras páginas usam essa base como pano de fundo.
Lembram do nosso {% extends 'base.html' %} do primeiro código HTML colado aqui? É algo que o Python faz, por meio do Flask, que é gerar essa base, com a barra de navegação superior, o rodapé, etc, então todas as outras páginas derivadas podem pegar essa forma de atalho e eu não precisar copiar e colar HTML repetido várias e várias vezes. E não só isso, o botão para selecionar arquivos, o campo de digitação de texto (input), os outros botões, foram criados por meio do Python e do Flask, usando essa sintaxe esquisita com chaves dentro do HTML. Posso dizer que esse é um primeiro exemplo de como o back-end interage com o front-end.
Mas o foco principal do tópico era mostrar o grosso do back-end, como pegamos dados e transformamos eles. Como, aqui no site da Bastter, ao criar um tópico ele vira algo que todos podemos ver. Como, no meu site, ao selecionar arquivos .csv eles viram um arquivo único .xlsx.
Vou selecionar alguns arquivos naquela minha URL
AnalyTools , digitar um nome qualquer para o arquivo de saída e apertar o botão azul "PROCESSAR ARQUIVOS".

Aqui o back-end da aplicação vai realmente atuar.
Um dos arquivos Python responsável por gerenciar isso tem essa cara aqui:
from app import app
from flask import (
render_template, request, redirect,
url_for, send_from_directory, flash
)
from .forms import PdfUploadForm, CsvUploadForm
import os
import tools
from tools import pdf_reader, csv_reader
...
@app.route('/spectrowsm/', methods=['GET', 'POST'])
def spectrowsm():
form = CsvUploadForm()
if form.validate_on_submit():
csv_files = request.files.getlist('csv_files')
form.validate_form(csv_files)
filename = request.form['filename_field'].strip().replace(' ', '')
savgol_option = int(request.form['savgol-option'])
windowlength = int(request.form['windowlength'])
polyorder = int(request.form['polyorder'])
derivative_option = int(request.form['derivative-option'])
derivative_order = int(request.form['derivative'])
delta_lambda = int(request.form['deltalambda'])
files = tools.save_files(csv_files, '.csv')
try:
csv_reader.pipeline(
files, filename, windowlength,
polyorder, savgol_option, derivative_option,
derivative_order, delta_lambda
)
# After debugging, change this to except AttributeError
except ZeroDivisionError:
tools.delete_temp_data()
flash('Você não selecionou um CSV válido')
return redirect(
url_for('spectrowsm')
)
filename = (os.listdir(f'{app.config["WORKSHEETS_FOLDER"]}')[-1])
return redirect(url_for('download', filename=filename))
return render_template(
'spectrowsm.html',
form=form)
Não se preocupem com a cara do código aqui, só deem uma olhada por cima. O que ele basicamente faz é validar os arquivos de entrada (se a pessoa selecionar um .jpg em vez de um .csv o programa não rodará), receber o nome do arquivo, receber outros dados possivelmente digitados pelo usuário no front-end, etc, e o processamento dos dados é feito em outro arquivo Python que tem essa cara aqui (não precisam ler o arquivo todo, é gigante, mas leiam só por cima pra sentir o que o back-end faz com os arquivos, e eu ainda cortei parte do código pra não ficar algo imenso):
import xlsxwriter
import time
import csv
from app import app
from tools import delete_previous_workbooks, delete_temp_data
from scipy.signal import savgol_filter
from string import ascii_uppercase
def read_csv(file: str) -> list:
"""
Read csv file and returns a list with the csv data.
"""
try:
with open(file, newline='', encoding='utf-8') as csv_file:
data = list(csv.reader(csv_file, delimiter=';'))
except UnicodeDecodeError:
with open(file, newline='', encoding='latin-1') as csv_file:
data = list(csv.reader(csv_file, delimiter=';'))
return data
def get_filename(data: list) -> str:
"""
Get the filename of csv object.
"""
# This slicing below is to strip the '.csv' of the filename
filename = data[0][0][:-4]
return filename
def get_real_values_data(data: list) -> list:
# Get real data values of csv file
for index, row in enumerate(data):
if row[0].replace(',', '').replace('-', '').isdigit() and row[1].replace(',', '').replace('-', '').isdigit():
initial_data_flag = index
break
data = list(data)[initial_data_flag:]
return data
def get_wavelength_range(data: list) -> list:
"""
Get the wavelength range of values.
"""
wavelength_range = sorted((
lambda_value[0] for lambda_value in data
))
return wavelength_range
def get_absorbance_values(data: list) -> list:
"""
Get the absorbance values.
"""
abs_values = [
float(abs_value[1].replace(',', '.')) for abs_value in data
]
return abs_values
def applies_savgol_filter(
data: list, window_length: int, polyorder: int
) -> list:
"""
Applies Saviztky-Golay filter to absorbances.
"""
data = [
float(str(value).replace(',', '.')) for value in data
]
savgol_values = savgol_filter(data, window_length, polyorder)
return savgol_values
def creates_workbook(filename) -> xlsxwriter.Workbook:
"""
Creates a xlsxwriter.Workbook object and returns it.
"""
delete_previous_workbooks()
date = time.strftime('%d%m%y%H%M%S')
workbook = xlsxwriter.Workbook(
f'{app.config["WORKSHEETS_FOLDER"]}/{filename}{date}.xlsx'
)
return workbook
def creates_new_worksheet(
workbook: xlsxwriter.Workbook,
filename: str,
wavelength_range: list,
full_values: dict
) -> xlsxwriter.Workbook.worksheet_class:
"""
Creates a new worksheet inside the workbook object.
"""
# Wavelength values are coming string type, for chart I need numbers
wavelength_range = [float(wv) for wv in wavelength_range]
worksheet = workbook.add_worksheet(f'{filename[:30]}')
worksheet.write(0, 0, 'Criado por:')
worksheet.write(0, 1, 'AnalyTools')
worksheet.write(1, 0, 'nm')
worksheet.write_column(2, 0, wavelength_range)
row = 1
col = 1
for sample, data in full_values.items():
worksheet.write(row, col, sample)
worksheet.write_column(row + 1, col, data)
col += 1
# Creates a chart type
chart = workbook.add_chart({
'type': 'scatter',
'subtype': 'smooth'
})
# Creates a big list of letters, which represents columns of worksheets
letters_list = list(ascii_uppercase) + [
('A' + letter) for letter in list(ascii_uppercase)
] + [
('B' + letter) for letter in list(ascii_uppercase)
] + [
('C' + letter) for letter in list(ascii_uppercase)
]
for i in range(len(full_values.items())):
chart.add_series({
'categories': f'={filename}!$A$3:$A${len(wavelength_range) + 2}',
'values': f'={filename}!${letters_list[i + 1]}$3:'
f'${letters_list[i + 1]}${len(wavelength_range) + 2}',
})
chart.set_title({'name': f'{filename}'})
chart.set_x_axis({
'name': 'nm',
'min': wavelength_range[0],
'max': wavelength_range[len(wavelength_range) - 1],
})
chart.set_y_axis({
'name': 'A'
})
chart.set_size({
'width': 650,
'height': 500
})
worksheet.insert_chart('E4', chart)
def closes_workbook(workbook):
workbook.close()
def pipeline(
files, filename, windowlength, polyorder, savgol_option,
derivative_option, derivative_order, delta_lambda
):
workbook = creates_workbook(filename)
csv_data_list = custom_map(read_csv, files)
filenames = custom_map(get_filename, csv_data_list)
csv_data_values = custom_map(get_real_values_data, csv_data_list)
wavelength_range = get_wavelength_range(csv_data_values[0])
abs_values_list = custom_map(get_absorbance_values, csv_data_values)
full_results = {
k: v for k, v in zip(filenames, abs_values_list)
}
creates_new_worksheet(workbook, filename, wavelength_range, full_results)
if savgol_option == 1:
savgol_values = custom_map(
lambda x: applies_savgol_filter(x, windowlength, polyorder),
abs_values_list
)
full_savgol_results = {
k: v for k, v in zip(filenames, savgol_values)
}
creates_new_worksheet(
workbook, f'{filename}savgol',
wavelength_range, full_savgol_results
)
if derivative_option == 1:
# Prepare data do derivate
data_to_derivate = custom_map(
lambda x: prepare_data_to_derivate(
x, wavelength_range, delta_lambda
), savgol_values
)
# Calculates derivative
derivative_values = custom_map(
lambda x: applies_derivative_on_savgol_values(
x, derivative_order, delta_lambda, derivate),
data_to_derivate
)
worksheet_data = organize_data_to_worksheet(derivative_values)
for i, deriv_dict in enumerate(worksheet_data):
deriv_wavelength_range = list(deriv_dict.keys())
deriv_absorbance_previous_values = list(deriv_dict.values())
deriv_absorbance_values = list(
zip(*deriv_absorbance_previous_values)
)
full_derivative_results = {
k: v for k, v in zip(filenames, deriv_absorbance_values)
}
creates_new_worksheet(
workbook, f'{filename}deriv{i + 1}',
deriv_wavelength_range, full_derivative_results
)
closes_workbook(workbook)
delete_temp_data()
No fim das contas, todo esse código pega vários e vários arquivos .csv que têm esta cara aqui:

Em algo assim, já com gráfico plotado:

Então, resumindo tudo, o front-end é a tela, é o que vai interagir com o usuário e a interação ocorre por meio de arquivos HTML, CSS e JavaScript, e em parte o back-end já entra nessa interação pois frameworks web ajudam a otimizar o trabalho de criação de código HTML por meio de templates no caso acima. O front-end que vai mostrar pro usuário se ele selecionou os arquivos corretamente, que vai pedir nome de arquivo de saída, etc.
Já o back-end, além de otimizar o trabalho do front-end, ainda processa os arquivos brutos e libera um arquivo processado já com a compilação de todos os dados e um gráfico para análise do usuário.
E novamente o front-end entra em ação, pois o arquivo é baixado pelo navegador e o usuário irá abrir o arquivo processado pelo back-end.
Enfim, espero que tenha ficado um pouco mais claro por meio dessa explicação para quem tinha dúvidas parecidas, tal como o @Luster e o @thiagofer70.