From 9c831716261cb4d2c862d2b1b929034feef1b623 Mon Sep 17 00:00:00 2001 From: Sara Date: Fri, 26 Jun 2026 17:34:07 -0300 Subject: [PATCH] =?UTF-8?q?Solu=C3=A7=C3=A3o=20do=20desafio=20t=C3=A9cnico?= =?UTF-8?q?=20(SQL=20test=20e=20cases=201,=202=20e=203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 5 ++ .gitignore | 21 ++++++++ .streamlit/config.toml | 2 + c1_dynamic_query.py | 52 +++++++++++++++++++ c2_average_ticket.py | 111 +++++++++++++++++++++++++++++++++++++++++ c3_visualization.py | 78 +++++++++++++++++++++++++++++ db.py | 25 ++++++++++ logo.png | Bin 36552 -> 0 bytes requirements.txt | 7 +++ 9 files changed, 301 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .streamlit/config.toml create mode 100644 c1_dynamic_query.py create mode 100644 c2_average_ticket.py create mode 100644 c3_visualization.py create mode 100644 db.py delete mode 100644 logo.png create mode 100644 requirements.txt diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3953c0b --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +DB_HOST= +DB_PORT=3306 +DB_USER= +DB_PASSWORD= +DB_NAME= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb9bac1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class + +# Virtual environments +.venv/ +venv/ +env/ + +# Environment variables (never commit real credentials) +.env + +# Generated outputs +*.png + +# IDE / OS +.vscode/ +.idea/ +.DS_Store +Thumbs.db diff --git a/.streamlit/config.toml b/.streamlit/config.toml new file mode 100644 index 0000000..26efe58 --- /dev/null +++ b/.streamlit/config.toml @@ -0,0 +1,2 @@ +[theme] +primaryColor = "#3FD569" diff --git a/c1_dynamic_query.py b/c1_dynamic_query.py new file mode 100644 index 0000000..398c971 --- /dev/null +++ b/c1_dynamic_query.py @@ -0,0 +1,52 @@ +import pandas as pd +import mysql.connector +from dotenv import load_dotenv +import os + +load_dotenv() + +def retrieve_data(product_code=None, store_code=None, date=None): + """ + Retrieve data_product_sales filtered by product_code (int or list), + store_code and date. Returns a DataFrame. + """ + conn = mysql.connector.connect( + host=os.environ.get("DB_HOST"), + port=os.environ.get("DB_PORT", 3306), + user=os.environ.get("DB_USER"), + password=os.environ.get("DB_PASSWORD"), + database=os.environ.get("DB_NAME"), + ) + + try: + conditions = [] + values = [] + + if product_code is not None: + if isinstance(product_code, (list, tuple)): + placeholders = ", ".join(["%s"] * len(product_code)) + conditions.append(f"PRODUCT_CODE IN ({placeholders})") + values.extend(product_code) + else: + conditions.append("PRODUCT_CODE = %s") + values.append(product_code) + + if store_code is not None: + conditions.append("STORE_CODE = %s") + values.append(store_code) + + if date is not None: + conditions.append("`DATE` BETWEEN %s AND %s") + values.append(date[0]) + values.append(date[1]) + + query = "SELECT * FROM data_product_sales" + + if conditions: + query = query + " WHERE " + " AND ".join(conditions) + + df = pd.read_sql_query(query, conn, params=values) + return df + + finally: + conn.close() \ No newline at end of file diff --git a/c2_average_ticket.py b/c2_average_ticket.py new file mode 100644 index 0000000..a4a0d9b --- /dev/null +++ b/c2_average_ticket.py @@ -0,0 +1,111 @@ +import os + +import pandas as pd +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +import mysql.connector +from dotenv import load_dotenv + +load_dotenv() + +QUERY_STORES = """ +SELECT + STORE_CODE, + STORE_NAME, + START_DATE, + END_DATE, + BUSINESS_NAME, + BUSINESS_CODE +FROM data_store_cad +""" + +QUERY_SALES = """ +SELECT + STORE_CODE, + DATE, + SALES_VALUE, + SALES_QTY +FROM data_store_sales +WHERE DATE BETWEEN '2019-01-01' AND '2019-12-31' +""" + +PERIOD_START = "2019-10-01" +PERIOD_END = "2019-12-31" + + +def get_connection(): + return mysql.connector.connect( + host=os.environ.get("DB_HOST"), + port=os.environ.get("DB_PORT", 3306), + user=os.environ.get("DB_USER"), + password=os.environ.get("DB_PASSWORD"), + database=os.environ.get("DB_NAME"), + ) + + +def load_data(): + conn = get_connection() + try: + stores = pd.read_sql_query(QUERY_STORES, conn) + sales = pd.read_sql_query(QUERY_SALES, conn) + return stores, sales + finally: + conn.close() + + +def build_average_ticket(stores, sales): + sales = sales.copy() + sales["DATE"] = pd.to_datetime(sales["DATE"]) + mask = (sales["DATE"] >= PERIOD_START) & (sales["DATE"] <= PERIOD_END) + sales = sales.loc[mask] + + agg = sales.groupby("STORE_CODE", as_index=False)[["SALES_VALUE", "SALES_QTY"]].sum() + agg["TM"] = (agg["SALES_VALUE"] / agg["SALES_QTY"]).round(2) + + result = agg.merge( + stores[["STORE_CODE", "STORE_NAME", "BUSINESS_NAME"]], + on="STORE_CODE", + how="inner", + ) + + result = result.rename(columns={"STORE_NAME": "Loja", "BUSINESS_NAME": "Categoria"}) + result = result[["Loja", "Categoria", "TM"]].sort_values("Loja").reset_index(drop=True) + return result + + +def plot_table(df, path="average_ticket.png"): + fig, ax = plt.subplots(figsize=(6, 0.4 * len(df) + 1)) + ax.axis("off") + + table = ax.table( + cellText=df.values, + colLabels=df.columns, + cellLoc="center", + loc="center", + ) + table.auto_set_font_size(False) + table.set_fontsize(10) + table.scale(1, 1.4) + + for col in range(len(df.columns)): + cell = table[0, col] + cell.set_facecolor("#2f5496") + cell.set_text_props(color="white", weight="bold") + + fig.tight_layout() + fig.savefig(path, dpi=150, bbox_inches="tight") + plt.close(fig) + return path + + +def main(): + stores, sales = load_data() + result = build_average_ticket(stores, sales) + print(result.to_string(index=False)) + path = plot_table(result) + print(f"\nVisualização salva em: {path}") + + +if __name__ == "__main__": + main() diff --git a/c3_visualization.py b/c3_visualization.py new file mode 100644 index 0000000..66f859e --- /dev/null +++ b/c3_visualization.py @@ -0,0 +1,78 @@ +import pandas as pd +import plotly.express as px +import streamlit as st + +from db import load_movies + +st.set_page_config(page_title="IMDB dashboard", page_icon="🎬", layout="wide") +st.title("IMDB Movies: Popularity and Rating") + + +@st.cache_data +def load_data(): + df = load_movies() + df["Decade"] = (df["Year"] // 10 * 10) + return df + + +df = load_data() + + +k1, k2, k3, k4 = st.columns(4) +k1.metric("Total Films", f"{len(df):,}") +k2.metric("Avg Rating", f"{df['Rating'].mean():.1f}") +k3.metric("Avg Votes", f"{df['Votes'].mean():,.0f}") +k4.metric("Median Votes", f"{df['Votes'].median():,.0f}") + +st.divider() + + +st.subheader("Votes vs Rating") +fig = px.scatter( + df.dropna(subset=["Votes", "Rating"]), + x="Votes", + y="Rating", + color="Decade", + color_continuous_scale=["#c8f5d5", "#3FD569", "#1a6b35"], + hover_data=["Title", "Year", "Director"], + trendline="ols", + log_x=True, + labels={"Votes": "Votes (log scale)", "Rating": "IMDB Rating"}, +) +st.plotly_chart(fig, use_container_width=True) + +st.caption( + "Votes on a log scale because the distribution is highly skewed " + "(a few blockbusters with millions of votes, most films with very few)." +) + +st.divider() + + +c1, c2 = st.columns(2) + +with c1: + st.subheader("Low visibility, high ratings") + st.caption("Rating ≥ 8 · Votes ≤ 50k") + gems = ( + df[(df["Rating"] >= 8) & (df["Votes"] <= 50_000)] + [["Title", "Year", "Director", "Genre", "Rating", "Votes"]] + .sort_values("Rating", ascending=False) + .reset_index(drop=True) + ) + st.dataframe(gems, use_container_width=True) + st.caption(f"{len(gems)} films found") + +with c2: + st.subheader("High visibility, below-median ratings") + st.caption("Top 25% by votes · Below median rating") + vote_q75 = df["Votes"].quantile(0.75) + median_rating = df["Rating"].median() + overhyped = ( + df[(df["Votes"] >= vote_q75) & (df["Rating"] < median_rating)] + [["Title", "Year", "Director", "Rating", "Votes"]] + .sort_values("Votes", ascending=False) + .reset_index(drop=True) + ) + st.dataframe(overhyped, use_container_width=True) + st.caption(f"{len(overhyped)} films found") \ No newline at end of file diff --git a/db.py b/db.py new file mode 100644 index 0000000..c897020 --- /dev/null +++ b/db.py @@ -0,0 +1,25 @@ +import os + +import mysql.connector +import pandas as pd +from dotenv import load_dotenv + +load_dotenv() + + +def _get_connection(): + return mysql.connector.connect( + host=os.environ.get("DB_HOST"), + port=int(os.environ.get("DB_PORT", 3306)), + user=os.environ.get("DB_USER"), + password=os.environ.get("DB_PASSWORD"), + database=os.environ.get("DB_NAME"), + ) + + +def load_movies() -> pd.DataFrame: + conn = _get_connection() + try: + return pd.read_sql_query("SELECT * FROM IMDB_movies", conn) + finally: + conn.close() diff --git a/logo.png b/logo.png deleted file mode 100644 index 309a466d14300abbacd4a347b185cd70287adaf4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36552 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 My85}Sb4q9e0Nm~-;Q#;t diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..44ff9c9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +streamlit +pandas +plotly>=5.18 +statsmodels +matplotlib +mysql-connector-python +python-dotenv