Compare commits
No commits in common. "master" and "master" have entirely different histories.
@ -74,6 +74,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "3333:3333"
|
- "3333:3333"
|
||||||
volumes:
|
volumes:
|
||||||
|
- ./.env:/app/.env
|
||||||
- ./static/parts:/app/static/parts
|
- ./static/parts:/app/static/parts
|
||||||
- ./static/instructions:/app/static/instructions
|
- ./static/instructions:/app/static/instructions
|
||||||
- ./static/sets:/app/static/sets
|
- ./static/sets:/app/static/sets
|
||||||
|
96
app.py
96
app.py
@ -12,10 +12,6 @@ import rebrick #rebrickable api
|
|||||||
import requests # request img from web
|
import requests # request img from web
|
||||||
import shutil # save img locally
|
import shutil # save img locally
|
||||||
import eventlet
|
import eventlet
|
||||||
from collections import defaultdict
|
|
||||||
import plotly.express as px
|
|
||||||
import pandas as pd
|
|
||||||
|
|
||||||
from downloadRB import download_and_unzip,get_nil_images,get_retired_sets
|
from downloadRB import download_and_unzip,get_nil_images,get_retired_sets
|
||||||
from db import initialize_database,get_rows,delete_tables
|
from db import initialize_database,get_rows,delete_tables
|
||||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
@ -1089,97 +1085,5 @@ def save_number(tmp):
|
|||||||
|
|
||||||
return Response(status=204)
|
return Response(status=204)
|
||||||
|
|
||||||
@app.route('/dashboard')
|
|
||||||
def dashboard():
|
|
||||||
|
|
||||||
# Connect to the SQLite database
|
|
||||||
conn = sqlite3.connect('app.db')
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Execute the query
|
|
||||||
cursor.execute("SELECT year, set_num, theme_id FROM sets")
|
|
||||||
rows = cursor.fetchall()
|
|
||||||
|
|
||||||
# Initialize defaultdict to count occurrences
|
|
||||||
theme_counts = defaultdict(int)
|
|
||||||
year_counts = defaultdict(int)
|
|
||||||
|
|
||||||
# Count unique occurrences (removing duplicates)
|
|
||||||
seen = set() # To track unique combinations
|
|
||||||
for year, set_num, theme_id in rows:
|
|
||||||
# Create a unique identifier for each entry
|
|
||||||
entry_id = f"{year}-{set_num}-{theme_id}"
|
|
||||||
if entry_id not in seen:
|
|
||||||
theme_counts[theme_id] += 1
|
|
||||||
year_counts[year] += 1
|
|
||||||
seen.add(entry_id)
|
|
||||||
|
|
||||||
# Convert to regular dictionaries and sort
|
|
||||||
sets_by_theme = dict(sorted(
|
|
||||||
{k: v for k, v in theme_counts.items() if v > 1}.items()
|
|
||||||
))
|
|
||||||
|
|
||||||
sets_by_year = dict(sorted(
|
|
||||||
{k: v for k, v in year_counts.items() if v > 0}.items()
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
# Graphs using Plotly
|
|
||||||
fig_sets_by_theme = px.bar(
|
|
||||||
x=list(sets_by_theme.keys()),
|
|
||||||
y=list(sets_by_theme.values()),
|
|
||||||
labels={'x': 'Theme ID', 'y': 'Number of Sets'},
|
|
||||||
title='Number of Sets by Theme'
|
|
||||||
)
|
|
||||||
fig_sets_by_year = px.line(
|
|
||||||
x=list(sets_by_year.keys()),
|
|
||||||
y=list(sets_by_year.values()),
|
|
||||||
labels={'x': 'Year', 'y': 'Number of Sets'},
|
|
||||||
title='Number of Sets Released Per Year'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
most_frequent_parts = {
|
|
||||||
"Brick 1 x 1": 866,
|
|
||||||
"Plate 1 x 1": 782,
|
|
||||||
"Plate 1 x 2": 633,
|
|
||||||
"Plate Round 1 x 1 with Solid Stud": 409,
|
|
||||||
"Tile 1 x 2 with Groove": 382,
|
|
||||||
}
|
|
||||||
minifigs_by_set = {"10217-1": 12, "7595-1": 8, "10297-1": 7, "21338-1": 4, "4865-1": 4}
|
|
||||||
missing_parts_by_set = {"10297-1": 4, "10280-1": 1, "21301-1": 1, "21338-1": 1, "7595-1": 1}
|
|
||||||
|
|
||||||
|
|
||||||
fig_parts = px.bar(
|
|
||||||
x=list(most_frequent_parts.keys()),
|
|
||||||
y=list(most_frequent_parts.values()),
|
|
||||||
labels={'x': 'Part Name', 'y': 'Quantity'},
|
|
||||||
title='Most Frequent Parts'
|
|
||||||
)
|
|
||||||
fig_minifigs = px.bar(
|
|
||||||
x=list(minifigs_by_set.keys()),
|
|
||||||
y=list(minifigs_by_set.values()),
|
|
||||||
labels={'x': 'Set Number', 'y': 'Number of Minifigures'},
|
|
||||||
title='Minifigures by Set'
|
|
||||||
)
|
|
||||||
fig_missing_parts = px.bar(
|
|
||||||
x=list(missing_parts_by_set.keys()),
|
|
||||||
y=list(missing_parts_by_set.values()),
|
|
||||||
labels={'x': 'Set Number', 'y': 'Missing Parts Count'},
|
|
||||||
title='Missing Parts by Set'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Convert graphs to HTML
|
|
||||||
graphs = {
|
|
||||||
"sets_by_theme": fig_sets_by_theme.to_html(full_html=False),
|
|
||||||
"sets_by_year": fig_sets_by_year.to_html(full_html=False),
|
|
||||||
"parts": fig_parts.to_html(full_html=False),
|
|
||||||
"minifigs": fig_minifigs.to_html(full_html=False),
|
|
||||||
"missing_parts": fig_missing_parts.to_html(full_html=False),
|
|
||||||
}
|
|
||||||
|
|
||||||
return render_template("dashboard.html", graphs=graphs)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
socketio.run(app.run(host='0.0.0.0', debug=True, port=3333))
|
socketio.run(app.run(host='0.0.0.0', debug=True, port=3333))
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
flask
|
flask
|
||||||
flask_socketio
|
flask_socketio
|
||||||
pathlib
|
pathlib
|
||||||
plotly
|
|
||||||
pandas
|
|
||||||
numpy
|
numpy
|
||||||
rebrick
|
rebrick
|
||||||
requests
|
requests
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>LEGO Dashboard</title>
|
|
||||||
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
margin: 20px;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.chart-container {
|
|
||||||
margin-bottom: 50px;
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.chart-container h2 {
|
|
||||||
text-align: center;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>LEGO Dashboard</h1>
|
|
||||||
|
|
||||||
<div class="chart-container">
|
|
||||||
<h2>Sets by Theme</h2>
|
|
||||||
{{ graphs['sets_by_theme']|safe }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="chart-container">
|
|
||||||
<h2>Sets Released Per Year</h2>
|
|
||||||
{{ graphs['sets_by_year']|safe }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="chart-container">
|
|
||||||
<h2>Most Frequent Parts</h2>
|
|
||||||
{{ graphs['parts']|safe }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="chart-container">
|
|
||||||
<h2>Minifigures by Set</h2>
|
|
||||||
{{ graphs['minifigs']|safe }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="chart-container">
|
|
||||||
<h2>Missing Parts by Set</h2>
|
|
||||||
{{ graphs['missing_parts']|safe }}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -142,7 +142,7 @@
|
|||||||
<div id="lightbox-modal">
|
<div id="lightbox-modal">
|
||||||
<div class="lightbox-wrapper">
|
<div class="lightbox-wrapper">
|
||||||
<span class="close">×</span>
|
<span class="close">×</span>
|
||||||
<img style="background-color: white;" class="lightbox-content" id="lightbox-image">
|
<img class="lightbox-content" id="lightbox-image">
|
||||||
<div class="text-container" id="lightbox-text"></div>
|
<div class="text-container" id="lightbox-text"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user