pranit144 commited on
Commit
a88a491
ยท
verified ยท
1 Parent(s): 7fedeed

Upload 8 files

Browse files
app.py CHANGED
@@ -10,10 +10,10 @@ app.secret_key = 'supersecretkey_for_session_management' # Required for flash me
10
  # Renamed to RAW as we'll augment it with position and path data
11
  VIDEOS_RAW = [
12
  {"id": "v1", "title": "A Beginner's Guide to Rainwater Collection", "url": "https://www.youtube.com/embed/uPS3GT1DC_Q?si=OYkUG-tuJeZquIkv", "length": "10:51", "base_points": 50},
13
- {"id": "v2", "title": "Introduction to Rainwater Harvesting", "url": "https://www.youtube.com/embed/uPS3GT1DC_Q?si=OYkUG-tuJeZquIkv", "length": "6:44", "base_points": 40},
14
  {"id": "v3", "title": "First Flush Diverter Explained", "url": "https://www.youtube.com/embed/zO09yau1lCo?si=g-EJqsLU3PNiWjRD", "length": "8:16", "base_points": 60},
15
  {"id": "v4", "title": "Is Rainwater Safe to Drink?", "url": "https://www.youtube.com/embed/nLphSEiHroU?si=PJ49EHuKVxeYRt_H", "length": "7:00", "base_points": 70}, # Adjusted length for the new video
16
- {"id": "v5", "title": "First Flush Installation", "url": "https://www.youtube.com/embed/gxguzuyDicQ?si=m8tMaKPh366Ox89a", "length": "9:34", "base_points": 65},
17
  {"id": "v6", "title": "DIY Home System Tour", "url": "https://www.youtube.com/embed/J2t_ZZGmBoo?si=LpSyztIH8xtF_Z_3", "length": "10:27", "base_points": 80},
18
  {"id": "v7", "title": "Upgrading a Large System", "url": "https://www.youtube.com/embed/-NJ8aS4_WDA?si=2C0vCgr2O2hiMJx6", "length": "9:01", "base_points": 75},
19
  {"id": "v8", "title": "Living on Rainwater in a Desert Environment", "url": "https://www.youtube.com/embed/rxmspcSjjKM?si=TerHbg_Ny_ZjbEy3", "length": "10:43", "base_points": 90},
@@ -88,57 +88,57 @@ for i, video_data in enumerate(VIDEOS_RAW):
88
 
89
 
90
  # Quizzes (Video ID -> List of Questions)
91
- # Each question: 'question', 'options' (list), 'correct_answer_index' (0-indexed), 'points'
92
  QUIZZES = {
93
  "v1": [
94
- {"question": "What is the primary benefit of rainwater collection?", "options": ["Reduced water bills", "Increased garden pests", "More cloudy days", "Better internet speed"], "correct_answer_index": 0, "points": 20},
95
- {"question": "What is a common component of a basic rainwater system?", "options": ["Solar panels", "Downspout", "Microwave", "Treadmill"], "correct_answer_index": 1, "points": 20},
96
- {"question": "Why is rainwater harvesting considered sustainable?", "options": ["It uses more energy", "It depletes groundwater", "It utilizes a renewable resource", "It requires constant human intervention"], "correct_answer_index": 2, "points": 20},
97
  ],
98
  "v2": [
99
- {"question": "What is the first step in harvesting rainwater?", "options": ["Filtering", "Collecting from a surface", "Storing in a tank", "Drinking it"], "correct_answer_index": 1, "points": 20},
100
- {"question": "Which of these is NOT typically part of an introduction to rainwater harvesting?", "options": ["System sizing", "Components overview", "Basic benefits", "Advanced purification techniques"], "correct_answer_index": 3, "points": 20},
101
- {"question": "What is a common use for harvested rainwater in a household?", "options": ["Cooking", "Showering", "Toilet flushing", "Drinking without treatment"], "correct_answer_index": 2, "points": 20},
102
  ],
103
  "v3": [
104
- {"question": "What is the main purpose of a first flush diverter?", "options": ["To increase water pressure", "To divert initial dirty rainwater", "To cool the water", "To attract insects"], "correct_answer_index": 1, "points": 20},
105
- {"question": "When does the first flush occur?", "options": ["During the middle of a storm", "After a prolonged dry spell", "At the very beginning of a rain event", "When the tank is full"], "correct_answer_index": 2, "points": 20},
106
- {"question": "Why is it important to divert the first flush?", "options": ["To save space in the tank", "To improve water quality", "To reduce the amount of collected water", "To make the system more complex"], "correct_answer_index": 1, "points": 20},
107
  ],
108
  "v4": [
109
- {"question": "What is crucial for making rainwater safe to drink?", "options": ["No filtration at all", "Only basic screening", "Proper filtration and purification", "Adding sugar"], "correct_answer_index": 2, "points": 20},
110
- {"question": "What contaminants might be found in raw rainwater?", "options": ["Pure oxygen", "Roof debris, dust, bird droppings", "Salt", "Distilled water"], "correct_answer_index": 1, "points": 20},
111
- {"question": "Which method is commonly used for purifying drinking water from rainwater?", "options": ["Boiling", "Freezing", "Microwaving", "Shaking vigorously"], "correct_answer_index": 0, "points": 20},
112
  ],
113
  "v5": [
114
- {"question": "What type of pipe is often used for first flush diverters?", "options": ["Electrical conduit", "PVC pipe", "Garden hose", "Metal rebar"], "correct_answer_index": 1, "points": 20},
115
- {"question": "Where is a first flush diverter typically installed?", "options": ["Inside the storage tank", "At the very top of the roof", "Between the gutter downspout and the storage tank", "Underground, far from the house"], "correct_answer_index": 2, "points": 20},
116
- {"question": "What is a common safety measure during installation?", "options": ["Working alone", "Wearing a blindfold", "Using proper tools and fall protection", "Installing during a thunderstorm"], "correct_answer_index": 2, "points": 20},
117
  ],
118
  "v6": [
119
- {"question": "What does a 'DIY Home System Tour' typically showcase?", "options": ["Professional installation only", "Pre-built commercial systems", "Custom-built, homeowner-installed systems", "Fantasy systems"], "correct_answer_index": 2, "points": 20},
120
- {"question": "What is a common component highlighted in a home tour?", "options": ["An electric car charger", "A storage tank", "A swimming pool heater", "A satellite dish"], "correct_answer_index": 1, "points": 20},
121
- {"question": "What can be learned from a DIY system tour?", "options": ["How to break the system", "Specific design choices and challenges", "The history of plumbing", "Advanced quantum physics"], "correct_answer_index": 1, "points": 20},
122
  ],
123
  "v7": [
124
- {"question": "What might an upgrade to a large system involve?", "options": ["Reducing tank capacity", "Adding more collection surfaces or storage", "Removing all filters", "Switching to bottled water"], "correct_answer_index": 1, "points": 20},
125
- {"question": "Why would someone upgrade a large rainwater harvesting system?", "options": ["To collect less water", "To improve efficiency or meet higher demand", "To make it less reliable", "To attract more birds"], "correct_answer_index": 1, "points": 20},
126
- {"question": "What should be considered when planning a system upgrade?", "options": ["Ignoring existing infrastructure", "Only focusing on aesthetics", "Budget, demand, and integration with current setup", "Completely random additions"], "correct_answer_index": 2, "points": 20},
127
  ],
128
  "v8": [
129
- {"question": "What is a primary challenge of living on rainwater in a desert?", "options": ["Too much rain", "Limited rainfall and high evaporation", "No need for water", "Excessive humidity"], "correct_answer_index": 1, "points": 20},
130
- {"question": "What strategy is crucial for rainwater harvesting in arid climates?", "options": ["Using tiny collection areas", "Maximizing collection surfaces and storage", "Wasting water frequently", "Ignoring forecasts"], "correct_answer_index": 1, "points": 20},
131
- {"question": "Why is careful management important in a desert environment?", "options": ["Water is abundant", "Water is a scarce and precious resource", "It's a fun hobby", "To prove a point"], "correct_answer_index": 1, "points": 20},
132
  ],
133
  "v9": [
134
- {"question": "What material are many water storage tanks made from?", "options": ["Glass", "Wood", "Polyethylene", "Cardboard"], "correct_answer_index": 2, "points": 20},
135
- {"question": "What tool might be used to prepare a cracked polyethylene tank for repair?", "options": ["A hammer", "A heat gun or plastic welder", "A spoon", "A paperclip"], "correct_answer_index": 1, "points": 20},
136
- {"question": "What is a key step in ensuring a successful tank repair?", "options": ["Leaving the crack dirty", "Cleaning and drying the area thoroughly", "Applying repair material blindly", "Ignoring manufacturer instructions"], "correct_answer_index": 1, "points": 20},
137
  ],
138
  "v10": [
139
- {"question": "What is the main function of a pump in a rainwater system?", "options": ["To filter the water", "To add chemicals to the water", "To pressurize and distribute water", "To cool the water"], "correct_answer_index": 2, "points": 20},
140
- {"question": "What kind of power source do most rainwater pumps use?", "options": ["Manual hand crank", "Electricity", "Solar power only", "Wind power"], "correct_answer_index": 1, "points": 20},
141
- {"question": "What needs to be considered when selecting a pump?", "options": ["Its color", "Flow rate and pressure requirements", "The brand of your car", "Its weight"], "correct_answer_index": 1, "points": 20},
142
  ],
143
  }
144
 
@@ -289,32 +289,64 @@ def quiz(video_id):
289
  flash("Please complete the video before taking the quiz.", 'warning')
290
  return redirect(url_for('dashboard'))
291
 
292
- if video_id in user_progress['video_quiz_scores']:
 
 
293
  flash(f"You already completed the quiz for '{video['title']}'. Your score: {user_progress['video_quiz_scores'][video_id]} points.", 'info')
294
  return redirect(url_for('dashboard'))
295
 
296
  if request.method == 'POST':
297
  user_score = 0
298
  correct_answers_count = 0
 
 
299
  for i, question_data in enumerate(quiz_questions):
300
- user_answer_index = request.form.get(f'question_{i}')
301
- if user_answer_index:
 
 
 
 
 
302
  try:
303
- user_answer_index = int(user_answer_index)
 
304
  if user_answer_index == question_data['correct_answer_index']:
305
- user_score += question_data['points']
 
 
306
  correct_answers_count += 1
307
  except ValueError:
308
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
 
310
  user_progress['points'] += user_score
311
  user_progress['video_quiz_scores'][video_id] = user_score
312
  flash(f"Quiz completed for '{video['title']}': You scored {user_score} points ({correct_answers_count}/{len(quiz_questions)} correct)!", 'success')
313
  session['user_progress'] = user_progress # Update session after modification
314
  evaluate_badges()
315
- return redirect(url_for('dashboard'))
316
 
317
- return render_template('quiz.html', video=video, quiz_questions=quiz_questions, enumerate=enumerate)
 
 
 
 
 
 
 
318
 
319
  @app.route('/set_username', methods=['POST'])
320
  def set_username():
 
10
  # Renamed to RAW as we'll augment it with position and path data
11
  VIDEOS_RAW = [
12
  {"id": "v1", "title": "A Beginner's Guide to Rainwater Collection", "url": "https://www.youtube.com/embed/uPS3GT1DC_Q?si=OYkUG-tuJeZquIkv", "length": "10:51", "base_points": 50},
13
+ {"id": "v2", "title": "Introduction to Rainwater Harvesting", "url": "https://www.youtube.com/embed/72CVryFbv5I?si=oKWZ5Ep1Z3kcLI-g", "length": "6:44", "base_points": 40},
14
  {"id": "v3", "title": "First Flush Diverter Explained", "url": "https://www.youtube.com/embed/zO09yau1lCo?si=g-EJqsLU3PNiWjRD", "length": "8:16", "base_points": 60},
15
  {"id": "v4", "title": "Is Rainwater Safe to Drink?", "url": "https://www.youtube.com/embed/nLphSEiHroU?si=PJ49EHuKVxeYRt_H", "length": "7:00", "base_points": 70}, # Adjusted length for the new video
16
+ {"id": "v5", "title": "First Flush Installation", "url": "https://www.youtube.com/embed/bbYJvTj8uiw?si=nClbPzN9L2buoilz", "length": "9:34", "base_points": 65},
17
  {"id": "v6", "title": "DIY Home System Tour", "url": "https://www.youtube.com/embed/J2t_ZZGmBoo?si=LpSyztIH8xtF_Z_3", "length": "10:27", "base_points": 80},
18
  {"id": "v7", "title": "Upgrading a Large System", "url": "https://www.youtube.com/embed/-NJ8aS4_WDA?si=2C0vCgr2O2hiMJx6", "length": "9:01", "base_points": 75},
19
  {"id": "v8", "title": "Living on Rainwater in a Desert Environment", "url": "https://www.youtube.com/embed/rxmspcSjjKM?si=TerHbg_Ny_ZjbEy3", "length": "10:43", "base_points": 90},
 
88
 
89
 
90
  # Quizzes (Video ID -> List of Questions)
91
+ # Each question: 'question', 'options' (list), 'correct_answer_index' (0-indexed), 'points', 'reason'
92
  QUIZZES = {
93
  "v1": [
94
+ {"question": "What is the primary benefit of rainwater collection?", "options": ["Reduced water bills", "Increased garden pests", "More cloudy days", "Better internet speed"], "correct_answer_index": 0, "points": 20, "reason": "Rainwater collection significantly reduces the need for municipal or well water, directly lowering utility costs."},
95
+ {"question": "What is a common component of a basic rainwater system?", "options": ["Solar panels", "Downspout", "Microwave", "Treadmill"], "correct_answer_index": 1, "points": 20, "reason": "A downspout is crucial for directing water from the roof (collection surface) to the storage system."},
96
+ {"question": "Why is rainwater harvesting considered sustainable?", "options": ["It uses more energy", "It depletes groundwater", "It utilizes a renewable resource", "It requires constant human intervention"], "correct_answer_index": 2, "points": 20, "reason": "Rainwater is a naturally replenished resource, making its use sustainable as it doesn't deplete finite resources."},
97
  ],
98
  "v2": [
99
+ {"question": "What is the first step in harvesting rainwater?", "options": ["Filtering", "Collecting from a surface", "Storing in a tank", "Drinking it"], "correct_answer_index": 1, "points": 20, "reason": "The initial step is always to collect water, typically from a roof or other suitable surface."},
100
+ {"question": "Which of these is NOT typically part of an introduction to rainwater harvesting?", "options": ["System sizing", "Components overview", "Basic benefits", "Advanced purification techniques"], "correct_answer_index": 3, "points": 20, "reason": "Advanced purification techniques are usually covered in more in-depth or specialized modules, not basic introductions."},
101
+ {"question": "What is a common use for harvested rainwater in a household?", "options": ["Cooking", "Showering", "Toilet flushing", "Drinking without treatment"], "correct_answer_index": 2, "points": 20, "reason": "Toilet flushing is a common and excellent use for untreated or minimally treated rainwater, reducing potable water consumption."},
102
  ],
103
  "v3": [
104
+ {"question": "What is the main purpose of a first flush diverter?", "options": ["To increase water pressure", "To divert initial dirty rainwater", "To cool the water", "To attract insects"], "correct_answer_index": 1, "points": 20, "reason": "A first flush diverter prevents the initial, most contaminated rainwater (carrying debris from the roof) from entering the main storage tank."},
105
+ {"question": "When does the first flush occur?", "options": ["During the middle of a storm", "After a prolonged dry spell", "At the very beginning of a rain event", "When the tank is full"], "correct_answer_index": 2, "points": 20, "reason": "The 'first flush' refers to the first few minutes of a rainfall event, when the most impurities are washed off collection surfaces."},
106
+ {"question": "Why is it important to divert the first flush?", "options": ["To save space in the tank", "To improve water quality", "To reduce the amount of collected water", "To make the system more complex"], "correct_answer_index": 1, "points": 20, "reason": "Diverting the first flush significantly improves the overall quality of the water collected in the storage tank."},
107
  ],
108
  "v4": [
109
+ {"question": "What is crucial for making rainwater safe to drink?", "options": ["No filtration at all", "Only basic screening", "Proper filtration and purification", "Adding sugar"], "correct_answer_index": 2, "points": 20, "reason": "For drinking, rainwater requires thorough filtration to remove particulates and purification (like boiling or UV) to eliminate pathogens."},
110
+ {"question": "What contaminants might be found in raw rainwater?", "options": ["Pure oxygen", "Roof debris, dust, bird droppings", "Salt", "Distilled water"], "correct_answer_index": 1, "points": 20, "reason": "Raw rainwater can pick up various contaminants from the atmosphere and collection surfaces, including organic matter, dust, and animal waste."},
111
+ {"question": "Which method is commonly used for purifying drinking water from rainwater?", "options": ["Boiling", "Freezing", "Microwaving", "Shaking vigorously"], "correct_answer_index": 0, "points": 20, "reason": "Boiling water for at least one minute is a reliable method to kill most bacteria and viruses, making it safe for consumption."},
112
  ],
113
  "v5": [
114
+ {"question": "What type of pipe is often used for first flush diverters?", "options": ["Electrical conduit", "PVC pipe", "Garden hose", "Metal rebar"], "correct_answer_index": 1, "points": 20, "reason": "PVC (Polyvinyl Chloride) pipes are commonly used due to their durability, cost-effectiveness, and ease of installation for water systems."},
115
+ {"question": "Where is a first flush diverter typically installed?", "options": ["Inside the storage tank", "At the very top of the roof", "Between the gutter downspout and the storage tank", "Underground, far from the house"], "correct_answer_index": 2, "points": 20, "reason": "It's installed in the downspout system before the water enters the main storage, allowing the initial flow to be diverted."},
116
+ {"question": "What is a common safety measure during installation?", "options": ["Working alone", "Wearing a blindfold", "Using proper tools and fall protection", "Installing during a thunderstorm"], "correct_answer_index": 2, "points": 20, "reason": "Safety precautions like using correct tools and fall protection are essential, especially when working on roofs or with heavy equipment."},
117
  ],
118
  "v6": [
119
+ {"question": "What does a 'DIY Home System Tour' typically showcase?", "options": ["Professional installation only", "Pre-built commercial systems", "Custom-built, homeowner-installed systems", "Fantasy systems"], "correct_answer_index": 2, "points": 20, "reason": "DIY tours focus on practical, often budget-friendly systems built and managed by homeowners themselves."},
120
+ {"question": "What is a common component highlighted in a home tour?", "options": ["An electric car charger", "A storage tank", "A swimming pool heater", "A satellite dish"], "correct_answer_index": 1, "points": 20, "reason": "The storage tank is a central and visually significant component of any rainwater harvesting system, often a key focus in tours."},
121
+ {"question": "What can be learned from a DIY system tour?", "options": ["How to break the system", "Specific design choices and challenges", "The history of plumbing", "Advanced quantum physics"], "correct_answer_index": 1, "points": 20, "reason": "DIY tours offer insights into practical design considerations, modifications, and solutions to common challenges faced by homeowners."},
122
  ],
123
  "v7": [
124
+ {"question": "What might an upgrade to a large system involve?", "options": ["Reducing tank capacity", "Adding more collection surfaces or storage", "Removing all filters", "Switching to bottled water"], "correct_answer_index": 1, "points": 20, "reason": "Upgrading often means expanding capacity to collect more water or increasing storage to meet higher demand."},
125
+ {"question": "Why would someone upgrade a large rainwater harvesting system?", "options": ["To collect less water", "To improve efficiency or meet higher demand", "To make it less reliable", "To attract more birds"], "correct_answer_index": 1, "points": 20, "reason": "Upgrades are typically motivated by a need for more water, better performance, or adapting to changing water demands."},
126
+ {"question": "What should be considered when planning a system upgrade?", "options": ["Ignoring existing infrastructure", "Only focusing on aesthetics", "Budget, demand, and integration with current setup", "Completely random additions"], "correct_answer_index": 2, "points": 20, "reason": "Effective planning requires assessing financial resources, water needs, and how new components will fit with existing infrastructure."},
127
  ],
128
  "v8": [
129
+ {"question": "What is a primary challenge of living on rainwater in a desert?", "options": ["Too much rain", "Limited rainfall and high evaporation", "No need for water", "Excessive humidity"], "correct_answer_index": 1, "points": 20, "reason": "Deserts are characterized by scarce rainfall and high temperatures, leading to significant water loss through evaporation, making collection challenging."},
130
+ {"question": "What strategy is crucial for rainwater harvesting in arid climates?", "options": ["Using tiny collection areas", "Maximizing collection surfaces and storage", "Wasting water frequently", "Ignoring forecasts"], "correct_answer_index": 1, "points": 20, "reason": "In arid regions, it's vital to capture as much rainfall as possible from large surfaces and store it effectively to last through dry periods."},
131
+ {"question": "Why is careful management important in a desert environment?", "options": ["Water is abundant", "Water is a scarce and precious resource", "It's a fun hobby", "To prove a point"], "correct_answer_index": 1, "points": 20, "reason": "Water is extremely limited in deserts, so every drop must be managed wisely to ensure survival and sustainability."},
132
  ],
133
  "v9": [
134
+ {"question": "What material are many water storage tanks made from?", "options": ["Glass", "Wood", "Polyethylene", "Cardboard"], "correct_answer_index": 2, "points": 20, "reason": "Polyethylene is a common material for water tanks due to its durability, resistance to corrosion, and food-grade safety for water storage."},
135
+ {"question": "What tool might be used to prepare a cracked polyethylene tank for repair?", "options": ["A hammer", "A heat gun or plastic welder", "A spoon", "A paperclip"], "correct_answer_index": 1, "points": 20, "reason": "Plastic welding or using a heat gun with filler material is the appropriate method for repairing cracks in polyethylene tanks."},
136
+ {"question": "What is a key step in ensuring a successful tank repair?", "options": ["Leaving the crack dirty", "Cleaning and drying the area thoroughly", "Applying repair material blindly", "Ignoring manufacturer instructions"], "correct_answer_index": 1, "points": 20, "reason": "A clean, dry surface is essential for proper adhesion and a long-lasting repair when working with adhesives or plastic welding."},
137
  ],
138
  "v10": [
139
+ {"question": "What is the main function of a pump in a rainwater system?", "options": ["To filter the water", "To add chemicals to the water", "To pressurize and distribute water", "To cool the water"], "correct_answer_index": 2, "points": 20, "reason": "Pumps are used to move water from the storage tank to where it's needed, often at a specific pressure for household use."},
140
+ {"question": "What kind of power source do most rainwater pumps use?", "options": ["Manual hand crank", "Electricity", "Solar power only", "Wind power"], "correct_answer_index": 1, "points": 20, "reason": "While solar pumps exist, most common rainwater systems utilize electric pumps for consistent power and performance."},
141
+ {"question": "What needs to be considered when selecting a pump?", "options": ["Its color", "Flow rate and pressure requirements", "The brand of your car", "Its weight"], "correct_answer_index": 1, "points": 20, "reason": "The pump must be chosen based on the desired water flow (how much water per minute) and pressure (how strong the water comes out) for the intended application."},
142
  ],
143
  }
144
 
 
289
  flash("Please complete the video before taking the quiz.", 'warning')
290
  return redirect(url_for('dashboard'))
291
 
292
+ # If quiz already taken, and it's a GET request, redirect to dashboard
293
+ # The user can't retake a quiz, so no need to show old results on GET
294
+ if video_id in user_progress['video_quiz_scores'] and request.method == 'GET':
295
  flash(f"You already completed the quiz for '{video['title']}'. Your score: {user_progress['video_quiz_scores'][video_id]} points.", 'info')
296
  return redirect(url_for('dashboard'))
297
 
298
  if request.method == 'POST':
299
  user_score = 0
300
  correct_answers_count = 0
301
+ quiz_feedback = [] # To store feedback for each question
302
+
303
  for i, question_data in enumerate(quiz_questions):
304
+ user_answer_index_str = request.form.get(f'question_{i}')
305
+ user_answer_index = None
306
+ is_correct = False
307
+ points_for_this_question = 0
308
+ user_selected_option_text = "No answer" # Default if nothing selected
309
+
310
+ if user_answer_index_str:
311
  try:
312
+ user_answer_index = int(user_answer_index_str)
313
+ user_selected_option_text = question_data['options'][user_answer_index]
314
  if user_answer_index == question_data['correct_answer_index']:
315
+ is_correct = True
316
+ points_for_this_question = question_data['points']
317
+ user_score += points_for_this_question
318
  correct_answers_count += 1
319
  except ValueError:
320
+ pass # user_answer_index remains None, user_selected_option_text is "No answer"
321
+
322
+ correct_option_text = question_data['options'][question_data['correct_answer_index']]
323
+ reason_text = question_data.get('reason', 'No specific reason provided for this question.') # Get reason, provide default
324
+
325
+ quiz_feedback.append({
326
+ 'question': question_data['question'],
327
+ 'user_answer_index': user_answer_index,
328
+ 'user_selected_option': user_selected_option_text,
329
+ 'correct_answer_index': question_data['correct_answer_index'],
330
+ 'correct_option': correct_option_text,
331
+ 'is_correct': is_correct,
332
+ 'points_earned': points_for_this_question,
333
+ 'reason': reason_text
334
+ })
335
 
336
  user_progress['points'] += user_score
337
  user_progress['video_quiz_scores'][video_id] = user_score
338
  flash(f"Quiz completed for '{video['title']}': You scored {user_score} points ({correct_answers_count}/{len(quiz_questions)} correct)!", 'success')
339
  session['user_progress'] = user_progress # Update session after modification
340
  evaluate_badges()
 
341
 
342
+ # Render the quiz page again, but this time with feedback
343
+ return render_template('quiz.html', video=video, quiz_questions=quiz_questions,
344
+ quiz_feedback=quiz_feedback, user_score=user_score,
345
+ total_quiz_points=sum(q['points'] for q in quiz_questions),
346
+ enumerate=enumerate, quiz_submitted=True)
347
+
348
+ # For GET request (displaying quiz for the first time)
349
+ return render_template('quiz.html', video=video, quiz_questions=quiz_questions, enumerate=enumerate, quiz_submitted=False)
350
 
351
  @app.route('/set_username', methods=['POST'])
352
  def set_username():
static/WhatsApp Image 2025-09-18 at 18.33.55_105edc80.jpg ADDED
templates/base.html CHANGED
@@ -44,6 +44,7 @@
44
  </style>
45
  </head>
46
  <body>
 
47
  <div class="container">
48
  {% with messages = get_flashed_messages(with_categories=true) %}
49
  {% if messages %}
 
44
  </style>
45
  </head>
46
  <body>
47
+ <img src="/static/WhatsApp Image 2025-09-18 at 18.33.55_105edc80.jpg" alt="Logo" style="position: fixed; top: 20px; right: 30px; width: 80px; height: auto; z-index: 1000; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.12);">
48
  <div class="container">
49
  {% with messages = get_flashed_messages(with_categories=true) %}
50
  {% if messages %}
templates/certificate.html ADDED
@@ -0,0 +1,278 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Certificate of Completion</title>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
10
+ <link href="https://fonts.googleapis.com/css2?family=Great+Vibes&family=Playfair+Display:wght@700&family=Open+Sans&display=swap" rel="stylesheet">
11
+ <style>
12
+ body {
13
+ font-family: 'Open Sans', sans-serif;
14
+ background: linear-gradient(135deg, #f0f2f5 0%, #e0e7ef 100%);
15
+ min-height: 100vh;
16
+ display: flex;
17
+ flex-direction: column;
18
+ align-items: center;
19
+ justify-content: center;
20
+ padding: 20px;
21
+ position: relative;
22
+ }
23
+ .certificate-container {
24
+ background: rgba(255, 255, 255, 0.95);
25
+ border-radius: 24px;
26
+ box-shadow: 0 10px 30px rgba(0,0,0,0.15);
27
+ max-width: 900px;
28
+ width: 100%;
29
+ margin-bottom: 30px;
30
+ border: 10px double #FFD700;
31
+ overflow: hidden;
32
+ position: relative;
33
+ }
34
+ .certificate {
35
+ padding: 60px 40px 40px 40px;
36
+ position: relative;
37
+ background: linear-gradient(135deg, #fdfdfd 0%, #f3f9ff 100%);
38
+ }
39
+ .logo-container {
40
+ width: 100px;
41
+ height: 100px;
42
+ margin: 0 auto 30px auto;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ border-radius: 20px;
47
+ background: #fff;
48
+ box-shadow: 0 4px 16px rgba(0,0,0,0.08);
49
+ position: relative;
50
+ }
51
+ .logo-container img {
52
+ width: 90px;
53
+ height: auto;
54
+ border-radius: 12px;
55
+ }
56
+ .certificate-title {
57
+ font-family: 'Playfair Display', serif;
58
+ font-size: 3em;
59
+ color: #1d3557;
60
+ margin-bottom: 10px;
61
+ text-transform: uppercase;
62
+ letter-spacing: 2px;
63
+ }
64
+ .certificate-subtitle {
65
+ font-size: 1.2em;
66
+ color: #444;
67
+ margin-bottom: 30px;
68
+ }
69
+ .certificate-body {
70
+ text-align: center;
71
+ margin: 40px 0 30px 0;
72
+ }
73
+ .certificate-name-label {
74
+ font-size: 1.1em;
75
+ color: #6c757d;
76
+ margin-bottom: 10px;
77
+ font-weight: 600;
78
+ }
79
+ .certificate-name {
80
+ font-family: 'Great Vibes', cursive;
81
+ font-size: 3.2em;
82
+ color: #0d6efd;
83
+ margin-bottom: 20px;
84
+ line-height: 1.2;
85
+ text-shadow: 2px 2px 6px rgba(0, 0, 0, 0.10);
86
+ }
87
+ .certificate-text {
88
+ font-size: 1.1em;
89
+ color: #495057;
90
+ line-height: 1.7;
91
+ margin: 0 auto 20px auto;
92
+ max-width: 700px;
93
+ }
94
+ .certificate-points {
95
+ font-size: 1.3em;
96
+ color: #0a9396;
97
+ font-weight: 700;
98
+ margin-top: 15px;
99
+ }
100
+ .certificate-footer {
101
+ width: 100%;
102
+ display: flex;
103
+ justify-content: space-between;
104
+ margin-top: 40px;
105
+ font-size: 1em;
106
+ color: #555;
107
+ }
108
+ .certificate-signature {
109
+ width: 40%;
110
+ text-align: center;
111
+ }
112
+ .certificate-signature p {
113
+ margin-top: 40px;
114
+ border-top: 2px solid #000;
115
+ padding-top: 8px;
116
+ font-weight: 600;
117
+ }
118
+ .certificate-id {
119
+ font-size: 0.95em;
120
+ color: #888;
121
+ text-align: center;
122
+ margin-top: 30px;
123
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
124
+ padding: 10px 20px;
125
+ background: #f8f9fa;
126
+ border-radius: 10px;
127
+ border: 1px solid #eee;
128
+ display: inline-block;
129
+ }
130
+ .download-btn {
131
+ background: linear-gradient(135deg, #00b4d8 0%, #0077b6 100%);
132
+ color: white;
133
+ border: none;
134
+ padding: 16px 32px;
135
+ border-radius: 50px;
136
+ font-size: 1.1em;
137
+ font-weight: 600;
138
+ cursor: pointer;
139
+ transition: all 0.3s ease;
140
+ box-shadow: 0 8px 32px rgba(0, 180, 216, 0.15);
141
+ margin: 30px auto 0 auto;
142
+ display: block;
143
+ letter-spacing: 0.5px;
144
+ }
145
+ .download-btn:hover {
146
+ background: linear-gradient(135deg, #0077b6 0%, #00b4d8 100%);
147
+ transform: translateY(-2px) scale(1.01);
148
+ box-shadow: 0 12px 32px rgba(0, 180, 216, 0.22);
149
+ }
150
+ @media (max-width: 700px) {
151
+ .certificate {
152
+ padding: 30px 10px;
153
+ }
154
+ .certificate-footer {
155
+ flex-direction: column;
156
+ gap: 20px;
157
+ }
158
+ }
159
+ @media print {
160
+ body {
161
+ background: white;
162
+ padding: 0;
163
+ }
164
+ .download-btn {
165
+ display: none !important;
166
+ }
167
+ .certificate-container {
168
+ box-shadow: none;
169
+ max-width: none;
170
+ width: 100%;
171
+ background: white;
172
+ border: none;
173
+ }
174
+ .certificate {
175
+ background: white;
176
+ }
177
+ }
178
+ </style>
179
+ </head>
180
+ <body>
181
+ <div class="certificate-container" id="certificate">
182
+ <div class="certificate">
183
+ <div class="logo-container">
184
+ <img src="/static/WhatsApp Image 2025-09-18 at 18.33.55_105edc80.jpg" alt="Logo">
185
+ </div>
186
+ <h1 class="certificate-title">Certificate of Completion</h1>
187
+ <p class="certificate-subtitle">This certifies that</p>
188
+ <div class="certificate-body">
189
+ <div class="certificate-name-label">Name:</div>
190
+ <div class="certificate-name" id="recipientName">{{ username }}</div>
191
+ <div class="certificate-name-label">has successfully completed the course</div>
192
+ <div class="certificate-text">
193
+ for demonstrating outstanding dedication and mastering the principles of Rainwater Harvesting
194
+ through the <strong>Raindrop Quest: Harvest Hero</strong> learning program.
195
+ </div>
196
+ <div class="certificate-points">Total Points Earned: <span id="points">{{ total_points }}</span></div>
197
+ </div>
198
+ <div class="certificate-footer">
199
+ <div class="certificate-signature">
200
+ <p>Date: <span id="issueDate">{{ 'today' | date('%B %d, %Y') }}</span></p>
201
+ </div>
202
+ <div class="certificate-signature">
203
+ <p>Instructor: HydroHero Academy</p>
204
+ </div>
205
+ </div>
206
+ <div class="certificate-id">
207
+ Certificate ID: RWH-2025-<span id="certId">001234</span>
208
+ </div>
209
+ </div>
210
+ </div>
211
+ <button class="download-btn" onclick="downloadPDF()">
212
+ ๐Ÿ“ฅ Download Certificate
213
+ </button>
214
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
215
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
216
+ <script>
217
+ // Function to generate random certificate ID
218
+ function generateCertId() {
219
+ return Math.random().toString(36).substr(2, 6).toUpperCase();
220
+ }
221
+ // Set current date
222
+ function setDate() {
223
+ const currentDate = new Date();
224
+ const options = { month: 'short', year: 'numeric' };
225
+ document.getElementById('issueDate').textContent = currentDate.toLocaleDateString('en-US', options);
226
+ }
227
+ // Set certificate ID
228
+ function setCertId() {
229
+ document.getElementById('certId').textContent = generateCertId();
230
+ }
231
+ // Function to download certificate as PDF
232
+ async function downloadPDF() {
233
+ const certificate = document.getElementById('certificate');
234
+ const downloadBtn = document.querySelector('.download-btn');
235
+ downloadBtn.style.display = 'none';
236
+ try {
237
+ const canvas = await html2canvas(certificate, {
238
+ scale: 3,
239
+ useCORS: true,
240
+ backgroundColor: '#ffffff',
241
+ width: certificate.offsetWidth,
242
+ height: certificate.offsetHeight,
243
+ logging: false
244
+ });
245
+ const { jsPDF } = window.jspdf;
246
+ const pdf = new jsPDF({
247
+ orientation: 'landscape',
248
+ unit: 'mm',
249
+ format: 'a4'
250
+ });
251
+ const imgWidth = 297;
252
+ const imgHeight = (canvas.height * imgWidth) / canvas.width;
253
+ const yPosition = (210 - imgHeight) / 2;
254
+ pdf.addImage(
255
+ canvas.toDataURL('image/png', 1.0),
256
+ 'PNG',
257
+ 0,
258
+ yPosition,
259
+ imgWidth,
260
+ imgHeight
261
+ );
262
+ const recipientName = document.getElementById('recipientName').textContent;
263
+ const fileName = `Certificate_${recipientName.replace(/\s+/g, '_')}_Rainwater_Harvesting.pdf`;
264
+ pdf.save(fileName);
265
+ } catch (error) {
266
+ alert('Error generating PDF. Please try again.');
267
+ } finally {
268
+ downloadBtn.style.display = 'block';
269
+ }
270
+ }
271
+ // Initialize on load
272
+ document.addEventListener('DOMContentLoaded', function() {
273
+ setDate();
274
+ setCertId();
275
+ });
276
+ </script>
277
+ </body>
278
+ </html>
templates/landing.html CHANGED
@@ -176,6 +176,7 @@
176
  </style>
177
  </head>
178
  <body>
 
179
  <div class="landing-container">
180
  <h1>๐Ÿ’ง Raindrop Quest: Harvest Hero</h1>
181
  <p>Learn about Rainwater Harvesting in a Fun Way!</p>
 
176
  </style>
177
  </head>
178
  <body>
179
+ <img src="/static/WhatsApp Image 2025-09-18 at 18.33.55_105edc80.jpg" alt="Logo" style="position: fixed; top: 20px; right: 30px; width: 80px; height: auto; z-index: 1000; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.12);">
180
  <div class="landing-container">
181
  <h1>๐Ÿ’ง Raindrop Quest: Harvest Hero</h1>
182
  <p>Learn about Rainwater Harvesting in a Fun Way!</p>
templates/quiz.html CHANGED
@@ -12,7 +12,7 @@
12
 
13
  .header {
14
  background: #1d3557;
15
- color:black;
16
  padding: 20px;
17
  text-align: center;
18
  border-bottom: 5px solid #457b9d;
@@ -40,7 +40,7 @@
40
  background: #1d3557;
41
  }
42
 
43
- .quiz-form {
44
  background: rgba(255, 255, 255, 0.9);
45
  margin: 40px auto;
46
  padding: 30px;
@@ -51,7 +51,7 @@
51
  border: 2px solid #457b9d;
52
  }
53
 
54
- .quiz-form h3 {
55
  font-size: 20px;
56
  color: #1d3557;
57
  margin-bottom: 10px;
@@ -96,6 +96,77 @@
96
  transform: scale(1.05);
97
  background: linear-gradient(90deg, #48cae4, #023047);
98
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  </style>
100
 
101
  <div class="header">
@@ -103,17 +174,44 @@
103
  <a href="{{ url_for('dashboard') }}">Back to Dashboard</a>
104
  </div>
105
 
106
- <form method="POST" class="quiz-form">
107
- {% for i, question_data in enumerate(quiz_questions) %}
108
- <h3>{{ i+1 }}. {{ question_data.question }}</h3>
109
- {% for j, option in enumerate(question_data.options) %}
110
- <label>
111
- <input type="radio" name="question_{{ i }}" value="{{ j }}" required>
112
- {{ option }}
113
- </label>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  {% endfor %}
115
- <br>
116
- {% endfor %}
117
- <button type="submit">Submit Quiz</button>
118
- </form>
119
- {% endblock %}
 
12
 
13
  .header {
14
  background: #1d3557;
15
+ color: white; /* Changed to white for better contrast */
16
  padding: 20px;
17
  text-align: center;
18
  border-bottom: 5px solid #457b9d;
 
40
  background: #1d3557;
41
  }
42
 
43
+ .quiz-form, .quiz-results {
44
  background: rgba(255, 255, 255, 0.9);
45
  margin: 40px auto;
46
  padding: 30px;
 
51
  border: 2px solid #457b9d;
52
  }
53
 
54
+ .quiz-form h3, .quiz-results h3 {
55
  font-size: 20px;
56
  color: #1d3557;
57
  margin-bottom: 10px;
 
96
  transform: scale(1.05);
97
  background: linear-gradient(90deg, #48cae4, #023047);
98
  }
99
+
100
+ /* Styles for quiz results */
101
+ .quiz-results h2 {
102
+ font-family: 'Montserrat', sans-serif;
103
+ color: #1d3557;
104
+ margin-bottom: 25px;
105
+ font-size: 2.2em;
106
+ }
107
+ .quiz-results p.score {
108
+ font-size: 1.5em;
109
+ font-weight: bold;
110
+ color: #023047;
111
+ margin-bottom: 30px;
112
+ }
113
+ .quiz-question-feedback {
114
+ margin-bottom: 30px;
115
+ padding: 20px;
116
+ border-radius: 15px;
117
+ border: 1px solid #ddd;
118
+ text-align: left;
119
+ }
120
+ .quiz-question-feedback.correct {
121
+ background-color: #e6ffed; /* Light green */
122
+ border-color: #28a745; /* Green */
123
+ }
124
+ .quiz-question-feedback.incorrect {
125
+ background-color: #ffe6e6; /* Light red */
126
+ border-color: #dc3545; /* Red */
127
+ }
128
+ .quiz-question-feedback h4 {
129
+ margin-top: 0;
130
+ color: #1d3557;
131
+ font-size: 1.3em;
132
+ }
133
+ .quiz-question-feedback .user-answer, .quiz-question-feedback .correct-answer {
134
+ font-size: 1.1em;
135
+ margin-bottom: 8px;
136
+ }
137
+ .quiz-question-feedback .user-answer span, .quiz-question-feedback .correct-answer span {
138
+ font-weight: bold;
139
+ }
140
+ .quiz-question-feedback .user-answer.correct-choice {
141
+ color: #28a745;
142
+ }
143
+ .quiz-question-feedback .user-answer.incorrect-choice {
144
+ color: #dc3545;
145
+ }
146
+ .quiz-question-feedback .correct-answer-text {
147
+ color: #28a745;
148
+ }
149
+ .quiz-question-feedback .reason {
150
+ font-style: italic;
151
+ color: #555;
152
+ margin-top: 15px;
153
+ padding-top: 10px;
154
+ border-top: 1px dashed #ccc;
155
+ }
156
+ .quiz-results .back-to-dashboard {
157
+ display: inline-block;
158
+ margin-top: 30px;
159
+ background: #007bff;
160
+ color: white;
161
+ padding: 12px 25px;
162
+ border-radius: 20px;
163
+ text-decoration: none;
164
+ font-weight: bold;
165
+ transition: background 0.3s;
166
+ }
167
+ .quiz-results .back-to-dashboard:hover {
168
+ background: #0056b3;
169
+ }
170
  </style>
171
 
172
  <div class="header">
 
174
  <a href="{{ url_for('dashboard') }}">Back to Dashboard</a>
175
  </div>
176
 
177
+ {% if quiz_submitted %}
178
+ <div class="quiz-results">
179
+ <h2>Quiz Results</h2>
180
+ <p class="score">Your Score: {{ user_score }} / {{ total_quiz_points }} points</p>
181
+
182
+ {% for feedback in quiz_feedback %}
183
+ <div class="quiz-question-feedback {% if feedback.is_correct %}correct{% else %}incorrect{% endif %}">
184
+ <h4>{{ loop.index }}. {{ feedback.question }}</h4>
185
+ <p class="user-answer {% if feedback.is_correct %}correct-choice{% else %}incorrect-choice{% endif %}">
186
+ Your Answer: <span>{{ feedback.user_selected_option }}</span>
187
+ {% if feedback.is_correct %} (Correct! ๐ŸŽ‰){% else %} (Incorrect ๐Ÿ˜”){% endif %}
188
+ </p>
189
+ {% if not feedback.is_correct %}
190
+ <p class="correct-answer">
191
+ Correct Answer: <span class="correct-answer-text">{{ feedback.correct_option }}</span>
192
+ </p>
193
+ {% endif %}
194
+ <p class="reason">
195
+ <strong>Reason:</strong> {{ feedback.reason }}
196
+ </p>
197
+ </div>
198
+ {% endfor %}
199
+
200
+ <a href="{{ url_for('dashboard') }}" class="back-to-dashboard">Return to Dashboard</a>
201
+ </div>
202
+ {% else %}
203
+ <form method="POST" class="quiz-form">
204
+ {% for i, question_data in enumerate(quiz_questions) %}
205
+ <h3>{{ i+1 }}. {{ question_data.question }}</h3>
206
+ {% for j, option in enumerate(question_data.options) %}
207
+ <label>
208
+ <input type="radio" name="question_{{ i }}" value="{{ j }}" required>
209
+ {{ option }}
210
+ </label>
211
+ {% endfor %}
212
+ <br>
213
  {% endfor %}
214
+ <button type="submit">Submit Quiz</button>
215
+ </form>
216
+ {% endif %}
217
+ {% endblock %}