diff --git a/app.py b/app.py index f2a0f9b..9d13ca1 100644 --- a/app.py +++ b/app.py @@ -244,31 +244,55 @@ def create_app(config=Config): @app.route('/leaderboard') def leaderboard(): - leaders = mongo.db.pins.aggregate([ + pipeline = [ {"$group": {"_id": "$added_by", "count": {"$sum": 1}}}, {"$sort": {"count": -1}}, {"$limit": 10}, - {"$lookup": { - "from": "users", - "localField": "_id", - "foreignField": "_id", - "as": "user_info" - }}, - {"$unwind": "$user_info"}, - {"$project": {"_id": 0, "username": "$user_info.username", "count": 1}} - ]) - #print(list(leaders)) - cleaned_leaders=list() - print(cleaned_leaders) - for leader in list(leaders): - leader["username"] = leader["username"][0]+"***"+leader["username"][-1] - cleaned_leaders.append(leader) - if current_user.is_authenticated: - username = current_user.username - else: - username = None + ] + + # Convert the aggregation result to a list + leaders_by_id = list(mongo.db.pins.aggregate(pipeline)) + + # Create a list to store the final results + cleaned_leaders = [] + + # Process each leader + for leader in leaders_by_id: + user_id = leader["_id"] + count = leader["count"] + + # Find the corresponding user + try: + user = mongo.db.users.find_one({"_id": ObjectId(user_id)}) + if user and "username" in user: + username = user["username"] + if len(username) >= 2: + cleaned_username = username[0] + "***" + username[-1] + else: + cleaned_username = username + cleaned_leaders.append({"username": cleaned_username, "count": count}) + except Exception as e: + print(f"Error processing user_id {user_id}: {e}") + continue + + print('THIS IS THE LEADERBOARD', cleaned_leaders) + # Get stats using aggregation pipeline + # Count total users + total_users_result = list(mongo.db.users.aggregate([ + {"$count": "total"} + ])) + total_users = total_users_result[0]["total"] if total_users_result else 0 - return render_template('leaderboard.html', leaders=cleaned_leaders, username=username) + # Count unique users who have added pins + active_users_result = list(mongo.db.pins.aggregate([ + {"$group": {"_id": None, "active_users": {"$addToSet": "$added_by"}}}, + {"$project": {"active_count": {"$size": "$active_users"}}} + ])) + active_users = active_users_result[0]["active_count"] if active_users_result else 0 + + # Calculate percentage (handle case where total_users might be 0) + active_percentage = round((active_users / total_users) * 100, 1) if total_users > 0 else 0 + return render_template('leaderboard.html', leaders=cleaned_leaders, percentage = active_percentage) return app diff --git a/static/images/bachemapa.svg b/static/images/bachemapa.svg new file mode 100644 index 0000000..abffa55 --- /dev/null +++ b/static/images/bachemapa.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + Bachemapa + + diff --git a/static/images/bachemapalogo.png b/static/images/bachemapalogo.png new file mode 100644 index 0000000..0fac9c1 Binary files /dev/null and b/static/images/bachemapalogo.png differ diff --git a/static/images/bg-trees.jpg b/static/images/bg-trees.jpg new file mode 100644 index 0000000..994b874 Binary files /dev/null and b/static/images/bg-trees.jpg differ diff --git a/static/images/favico.ico b/static/images/favico.ico new file mode 100644 index 0000000..0a953df Binary files /dev/null and b/static/images/favico.ico differ diff --git a/static/styles.css b/static/styles.css index e715fe3..d74be33 100644 --- a/static/styles.css +++ b/static/styles.css @@ -1,175 +1,322 @@ +:root { + /* Color palette */ + --primary-color: rgba(202, 216, 3, 0.9); + --secondary-color: green; + --accent-color: rgb(255, 136, 0); + --background-color: rgb(205, 243, 148); + --text-color: #333; + --shadow-color: rgba(0, 0, 0, 0.2); + + /* Spacing */ + --spacing-sm: 0.5rem; + --spacing-md: 1rem; + --spacing-lg: 1.5rem; +} + +/* Base styles */ body, html { - font-family: Arial, sans-serif; + font-family: 'Segoe UI', Arial, sans-serif; margin: 0; - padding: 0px; + padding: 0; overflow: hidden; height: 100%; width: 100%; + color: var(--text-color); +} + +::selection { + background-color: var(--accent-color); + color: white; } h1 { text-align: center; + position: relative; } -section#pinner-modal form { - max-width: 400px; - margin: 0 auto; - padding: 20px; - border: 1px solid #ccc; - border-radius: 5px; - background-color: rgba(202, 216, 3, 0.7); - font-size: 100%; -} - -form p { - margin: 10px 0; +h1::after { + content: ''; + display: block; + width: 50px; + height: 3px; + background-color: var(--accent-color); + margin: 8px auto; + border-radius: 2px; } +/* Layout */ #map { width: 100%; height: 100vh; - margin: 0 0; position: relative; } main { - margin:0; - padding:0; + margin: 0; + padding: 0; position: absolute; top: 0; left: 0; -} - -.info { - padding: 1rem; - padding-bottom: 6rem; -} - -div#qrgen img { - padding-left: 0; - padding-right: 0; - margin-left: auto; - margin-right: auto; - display: block; -} - -section#pinner-modal { - position: absolute; - top: 7vh; - left: 0rem; - color: white; - padding: 1rem; - text-align: left; - transition: bottom 0.5s ease; - box-shadow: 0px -4px 15px rgba(0, 0, 0, 0.2); - z-index: 999; - display: flex; - flex-shrink: 1; -} - -section.pinner-modal.active { - bottom: 0; - background-color: rgba(202, 216, 3, 0.7); -} - -section.pinner-modal form { - background-color: rgba(202, 216, 3, 0.7); -} - -nav { - position:absolute; - top:0; - left: 0; - right: 0; - background-color: rgba(202, 216, 3, 0.7); - padding: 5px; - z-index: 1000; - font-size: 100%; - overflow: hidden; - padding-right: 0.1rem; -} - -nav ul { - flex-wrap: wrap; -} - -nav ul li { - padding-top: 0; - padding-bottom: 0; - flex:1 1 auto; -} - -.leaflet-marker-icon { - background-color: transparent !important; - border: rgba(0, 0, 0, 0); - padding: 0; - margin: 0; -} - -.marker-cluster { - background-color: greenyellow !important; - border-radius: 100px; -} -.fa, .far, .fas { - color:black; - text-shadow: -1px 0 rgb(255, 136, 0), 0 1px rgb(255, 136, 0), 1px 0 rgb(255, 136, 0), 0 -1px rgb(255, 136, 0); - margin-left: -10px; - margin-top: -10px; - font-size: 1.5rem; - filter:drop-shadow(0 0 0.2rem orange); -} - -path-derping { - fill:orangered; - stroke: orangered; - stroke-width:1px; - stroke-dasharray: 2,2; - stroke-linejoin: round; -} - -.flashes { - position: relative; - display: flex; - z-index: 1001; - border-radius: 20px; - background-color: rgba(202, 216, 3, 0.7); - top: 8%; - padding: 1rem; - border: black; - border-width: 1px; - -webkit-animation: cssAnimation 7s forwards; - animation: cssAnimation 7s forwards; -} -@keyframes cssAnimation { - 0% {opacity: 1;} - 90% {opacity: 1;} - 100% {opacity: 0;} -} -@-webkit-keyframes cssAnimation { - 0% {opacity: 1;} - 90% {opacity: 1;} - 100% {opacity: 0;} -} - -#pinner-top { - background-color: green; -} - -input#submit { - background-color: green; -} - -button a { - color: black; -} - -main { display: flex; align-items: center; justify-content: center; + height: 100vh; + width: 100vw; + z-index: 5; +} + +footer { + position: fixed; + bottom: 0; + max-height: 10vh; + text-align: left; + align-content: left; + font-size: 0.8rem; + background-color:rgba(202, 216, 3, 0.7); + width: 100%; + border-top: 1px solid greenyellow; + padding-left: 15%; + padding-right:15%; + z-index: 15; +} + +article { + position: fixed; + top: 12%; /* Space for nav */ + bottom: 10%; /* Space for footer */ + left: 50%; + transform: translateX(-50%); + text-align: center; + z-index: 10; + max-height: calc(100vh - 90px); /* Adjust based on nav and footer heights */ + overflow-y: auto; + width: 80%; + display: block; + flex-direction: column; + justify-content: center; +} +.info { + padding: var(--spacing-md); + padding-bottom: 6rem; + background-color: rgba(255, 255, 255, 0.85); + border-radius: 8px; + box-shadow: 0 4px 12px var(--shadow-color); +} + +/* Navigation */ +nav { + position: absolute; + top: 0; + left: 0; + right: 0; + padding-left: 10vw; + padding-right:10vw; + background-color: rgba(189, 216, 3, 0.24); + /* padding: var(--spacing-sm); */ + z-index: 1000; + font-size: 100%; + overflow: hidden; + box-shadow: 0 2px 10px var(--shadow-color); + backdrop-filter: blur(5px); +} + +nav ul { + display: flex; + flex-wrap: wrap; + gap: var(--spacing-sm); + padding: 0 var(--spacing-sm); +} + +nav ul li { + padding: 0; + flex: 1 1 auto; + list-style: none; +} + +/* Buttons */ +button { + transition: all 0.3s ease; + position: relative; + overflow: hidden; + background-color: var(--accent-color); +} + +button::after { + content: ''; + position: absolute; + height: 100%; + width: 100%; + top: 0; + left: -100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); + transition: left 0.5s ease; +} + +button:hover::after { + left: 100%; +} + +#pinner-top { + background-color: var(--secondary-color); + color: white; + border-radius: 4px; +} + +button a { + color: white; + text-decoration: none; +} + +/* Form elements */ +input, select, textarea { + border: 1px solid #ddd; + border-radius: 4px; + padding: 8px; + transition: border 0.3s ease; + width: 100%; +} + +input:focus, select:focus, textarea:focus { + outline: none; + border-color: var(--accent-color); + box-shadow: 0 0 0 2px rgba(255, 136, 0, 0.2); +} + +input#submit { + background-color: var(--secondary-color); + color: white; + font-weight: bold; + cursor: pointer; + padding: 10px; +} + +input#submit:hover { + background-color: #008800; + transform: translateY(-2px); } #lng, #lat { font-size: 0.8rem; background-color: grey; + color: white; + border-radius: 3px; + padding: 4px 8px; +} + +/* Modal */ +section#pinner-modal { + position: absolute; + top: 10vh; + left:10%; + color: var(--text-color); + padding: var(--spacing-md); + text-align: left; + animation: fadeIn 0.5s forwards; + box-shadow: 0 4px 20px var(--shadow-color); + z-index: 999; + display: flex; + flex-shrink: 1; + border-radius: 8px; + max-height: 80vh; + overflow-y: auto; + background-color: rgba(189, 216, 3, 0.24); + backdrop-filter: blur(5px); +} + +section#pinner-modal form { + max-width: 400px; + margin: 0 auto; + padding: var(--spacing-md); + border-radius: 8px; + background-color: var(--primary-color); + font-size: 100%; +} + +section#pinner-modal::-webkit-scrollbar { + width: 8px; +} + +section#pinner-modal::-webkit-scrollbar-thumb { + background-color: var(--accent-color); + border-radius: 10px; +} + +/* Map elements */ +.leaflet-marker-icon { + background-color: transparent !important; + border: none; + padding: 0; + margin: 0; + transition: transform 0.3s ease; +} + +.leaflet-marker-icon:hover { + transform: scale(1.2); +} + +.marker-cluster { + background-color: greenyellow !important; + border-radius: 100px; + box-shadow: 0 0 10px rgba(172, 255, 47, 0.7); + animation: pulse 2s infinite; +} + +.fa, .far, .fas { + color: black; + text-shadow: -1px 0 var(--accent-color), 0 1px var(--accent-color), + 1px 0 var(--accent-color), 0 -1px var(--accent-color); + margin-left: -10px; + margin-top: -10px; + font-size: 1.5rem; + filter: drop-shadow(0 0 0.2rem var(--accent-color)); +} + +/* Notifications */ +.flashes { + position: relative; + display: flex; + z-index: 1001; + border-radius: 20px; + background-color: rgba(189, 216, 3, 0.6); + top: 8%; + padding: var(--spacing-md); + border: 2px solid rgba(0, 0, 0, 0.1); + box-shadow: 0 4px 15px var(--shadow-color); + animation: fadeOut 15s forwards; +} + +/* Animations */ +@keyframes fadeOut { + 0% { opacity: 1; transform: translateY(0); } + 90% { opacity: 1; transform: translateY(0); } + 100% { opacity: 0; transform: translateY(-20px); } +} +@keyframes fadeIn { + 0% { opacity: 0; transform: translateY(0); } + 90% { opacity: 1; transform: translateY(0); } +} + +@keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.05); } + 100% { transform: scale(1); } +} + +/* Media queries for responsiveness */ +@media (max-width: 768px) { + section#pinner-modal { + width: 90%; + left: 5%; + right: 5%; + } + img#logo { + max-height: 5vh; + } + nav { + height: 10vh; + } + + nav ul { + justify-content: center; + } } diff --git a/templates/base.html b/templates/base.html index 34632a6..8a33964 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,6 +1,7 @@ + Bachemapa - Mapa interactivo de baches y otras cosas @@ -47,13 +48,26 @@