pre-clean x2

Signed-off-by: boris <boris@borishub.co.uk>
This commit is contained in:
boris
2025-04-21 23:02:08 +01:00
parent 8877faa631
commit 183cca3fd3
19 changed files with 170 additions and 19406 deletions

View File

@@ -1,232 +0,0 @@
import sqlite3
import random
# Connect to the SQLite database
conn = sqlite3.connect('Databases/ecobuddy.sqlite')
cursor = conn.cursor()
# Check if we need to add any new categories
cursor.execute("SELECT id FROM ecoCategories")
existing_categories = [row[0] for row in cursor.fetchall()]
# Add two new categories
new_categories = [
(16, "Urban Farms"),
(17, "Rainwater Harvesting Systems")
]
for category in new_categories:
if category[0] not in existing_categories:
cursor.execute("INSERT INTO ecoCategories (id, name) VALUES (?, ?)", category)
print(f"Added new category: {category[1]}")
# Get list of user IDs for contributors
cursor.execute("SELECT id FROM ecoUser")
user_ids = [row[0] for row in cursor.fetchall()]
# Define 10 new ecological facilities in the UK with accurate location data
new_facilities = [
{
"title": "Community Garden Hackney",
"category": 12, # Pollinator Gardens
"description": "Urban garden with native plants to support local pollinators",
"houseNumber": "45",
"streetName": "Dalston Lane",
"county": "Greater London",
"town": "London",
"postcode": "E8 3AH",
"lng": -0.0612,
"lat": 51.5476,
"contributor": random.choice(user_ids),
"status_comments": [
"Recently expanded with new wildflower section",
"Volunteer days every Saturday"
]
},
{
"title": "Rooftop Solar Farm",
"category": 8, # Green Roofs
"description": "Combined green roof and solar panel installation on commercial building",
"houseNumber": "120",
"streetName": "Deansgate",
"county": "Greater Manchester",
"town": "Manchester",
"postcode": "M3 2QJ",
"lng": -2.2484,
"lat": 53.4808,
"contributor": random.choice(user_ids),
"status_comments": [
"Generates power for the entire building"
]
},
{
"title": "Edinburgh Tool Library",
"category": 15, # Community Tool Libraries
"description": "Borrow tools instead of buying them - reducing waste and consumption",
"houseNumber": "25",
"streetName": "Leith Walk",
"county": "Edinburgh",
"town": "Edinburgh",
"postcode": "EH6 8LN",
"lng": -3.1752,
"lat": 55.9677,
"contributor": random.choice(user_ids),
"status_comments": []
},
{
"title": "Cardiff Bay Water Refill Station",
"category": 9, # Public Water Refill Stations
"description": "Free water refill station to reduce plastic bottle usage",
"houseNumber": "3",
"streetName": "Mermaid Quay",
"county": "Cardiff",
"town": "Cardiff",
"postcode": "CF10 5BZ",
"lng": -3.1644,
"lat": 51.4644,
"contributor": random.choice(user_ids),
"status_comments": [
"Recently cleaned and maintained",
"High usage during summer months"
]
},
{
"title": "Bristol Urban Farm",
"category": 16, # Urban Farms (new category)
"description": "Community-run urban farm providing local produce and education",
"houseNumber": "18",
"streetName": "Stapleton Road",
"county": "Bristol",
"town": "Bristol",
"postcode": "BS5 0RA",
"lng": -2.5677,
"lat": 51.4635,
"contributor": random.choice(user_ids),
"status_comments": [
"Open for volunteers Tuesday-Sunday"
]
},
{
"title": "Newcastle Rainwater Collection System",
"category": 17, # Rainwater Harvesting Systems (new category)
"description": "Public demonstration of rainwater harvesting for garden irrigation",
"houseNumber": "55",
"streetName": "Northumberland Street",
"county": "Tyne and Wear",
"town": "Newcastle upon Tyne",
"postcode": "NE1 7DH",
"lng": -1.6178,
"lat": 54.9783,
"contributor": random.choice(user_ids),
"status_comments": []
},
{
"title": "Brighton Beach Solar Bench",
"category": 7, # Solar-Powered Benches
"description": "Solar-powered bench with USB charging ports and WiFi",
"houseNumber": "",
"streetName": "Kings Road",
"county": "East Sussex",
"town": "Brighton",
"postcode": "BN1 2FN",
"lng": -0.1426,
"lat": 50.8214,
"contributor": random.choice(user_ids),
"status_comments": [
"Popular spot for tourists",
"One USB port currently not working"
]
},
{
"title": "Leeds Community Compost Hub",
"category": 6, # Community Compost Bins
"description": "Large-scale community composting facility for local residents",
"houseNumber": "78",
"streetName": "Woodhouse Lane",
"county": "West Yorkshire",
"town": "Leeds",
"postcode": "LS2 9JT",
"lng": -1.5491,
"lat": 53.8067,
"contributor": random.choice(user_ids),
"status_comments": [
"Recently expanded capacity"
]
},
{
"title": "Glasgow EV Charging Hub",
"category": 4, # Public EV Charging Stations
"description": "Multi-vehicle EV charging station with fast chargers",
"houseNumber": "42",
"streetName": "Buchanan Street",
"county": "Glasgow",
"town": "Glasgow",
"postcode": "G1 3JX",
"lng": -4.2526,
"lat": 55.8621,
"contributor": random.choice(user_ids),
"status_comments": [
"6 charging points available",
"24/7 access"
]
},
{
"title": "Oxford E-Waste Collection Center",
"category": 13, # E-Waste Collection Bins
"description": "Dedicated facility for proper disposal and recycling of electronic waste",
"houseNumber": "15",
"streetName": "St Aldate's",
"county": "Oxfordshire",
"town": "Oxford",
"postcode": "OX1 1BX",
"lng": -1.2577,
"lat": 51.7520,
"contributor": random.choice(user_ids),
"status_comments": []
}
]
# Insert facilities into the database
for facility in new_facilities:
cursor.execute("""
INSERT INTO ecoFacilities
(title, category, description, houseNumber, streetName, county, town, postcode, lng, lat, contributor)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
facility["title"],
facility["category"],
facility["description"],
facility["houseNumber"],
facility["streetName"],
facility["county"],
facility["town"],
facility["postcode"],
facility["lng"],
facility["lat"],
facility["contributor"]
))
# Get the ID of the inserted facility
facility_id = cursor.lastrowid
# Add status comments if any
for comment in facility["status_comments"]:
cursor.execute("""
INSERT INTO ecoFacilityStatus (facilityId, statusComment)
VALUES (?, ?)
""", (facility_id, comment))
print(f"Added facility: {facility['title']} in {facility['town']}")
# Commit the changes and close the connection
conn.commit()
conn.close()
print("\nSuccessfully added 10 new ecological facilities to the database.")
Using the test data in the ecoCategories, ecoFacilities, and ecoFacilityStatus, please create 10 new ecological facilities with the following constraints:
Must be located in the United Kingdom (Names of areas, towns, cities, counties)
Postcode, Latitude and Longitude must be fairly accurate to the location you have generated for the area.
More categories may be made, it is up to you, but make sure foreign keys are respected.
Contributor may be of any user in the database.
Not all facilities must have statusComments, however facilities may have multiple statusComments.

Binary file not shown.

Binary file not shown.

View File

@@ -1,66 +0,0 @@
Starting facility generation at 2025-03-20 12:34:00.832910
Target: 1000 new facilities
Generating facilities...
Generated 100 facilities so far...
Generated 200 facilities so far...
Generated 300 facilities so far...
Generated 400 facilities so far...
Generated 500 facilities so far...
Generated 600 facilities so far...
Generated 700 facilities so far...
Generated 800 facilities so far...
Generated 900 facilities so far...
Generated 1000 facilities so far...
Inserting facilities into database...
Inserted batch 1/20
Inserted batch 2/20
Inserted batch 3/20
Inserted batch 4/20
Inserted batch 5/20
Inserted batch 6/20
Inserted batch 7/20
Inserted batch 8/20
Inserted batch 9/20
Inserted batch 10/20
Inserted batch 11/20
Inserted batch 12/20
Inserted batch 13/20
Inserted batch 14/20
Inserted batch 15/20
Inserted batch 16/20
Inserted batch 17/20
Inserted batch 18/20
Inserted batch 19/20
Inserted batch 20/20
Inserting status comments...
Inserted comment batch 1/23
Inserted comment batch 2/23
Inserted comment batch 3/23
Inserted comment batch 4/23
Inserted comment batch 5/23
Inserted comment batch 6/23
Inserted comment batch 7/23
Inserted comment batch 8/23
Inserted comment batch 9/23
Inserted comment batch 10/23
Inserted comment batch 11/23
Inserted comment batch 12/23
Inserted comment batch 13/23
Inserted comment batch 14/23
Inserted comment batch 15/23
Inserted comment batch 16/23
Inserted comment batch 17/23
Inserted comment batch 18/23
Inserted comment batch 19/23
Inserted comment batch 20/23
Inserted comment batch 21/23
Inserted comment batch 22/23
Inserted comment batch 23/23
Generation complete at 2025-03-20 12:34:00.860477
Total facilities in database: 12025
Total status comments in database: 13385
Generated facilities saved to generated_facilities.csv for reference

View File

@@ -1,568 +0,0 @@
import sqlite3
import random
import csv
import os
from datetime import datetime
# Connect to the SQLite database
conn = sqlite3.connect('ecobuddy.sqlite')
cursor = conn.cursor()
# Get current max facility ID
cursor.execute("SELECT MAX(id) FROM ecoFacilities")
max_facility_id = cursor.fetchone()[0] or 0
# Get list of user IDs for contributors
cursor.execute("SELECT id FROM ecoUser")
user_ids = [row[0] for row in cursor.fetchall()]
# Get list of categories
cursor.execute("SELECT id, name FROM ecoCategories")
categories = {row[0]: row[1] for row in cursor.fetchall()}
# UK Cities and Towns with their counties and approximate coordinates
uk_locations = [
# Format: Town/City, County, Latitude, Longitude, Postcode Area
("London", "Greater London", 51.5074, -0.1278, "EC"),
("Birmingham", "West Midlands", 52.4862, -1.8904, "B"),
("Manchester", "Greater Manchester", 53.4808, -2.2426, "M"),
("Glasgow", "Glasgow", 55.8642, -4.2518, "G"),
("Liverpool", "Merseyside", 53.4084, -2.9916, "L"),
("Bristol", "Bristol", 51.4545, -2.5879, "BS"),
("Edinburgh", "Edinburgh", 55.9533, -3.1883, "EH"),
("Leeds", "West Yorkshire", 53.8008, -1.5491, "LS"),
("Sheffield", "South Yorkshire", 53.3811, -1.4701, "S"),
("Newcastle upon Tyne", "Tyne and Wear", 54.9783, -1.6178, "NE"),
("Nottingham", "Nottinghamshire", 52.9548, -1.1581, "NG"),
("Cardiff", "Cardiff", 51.4816, -3.1791, "CF"),
("Belfast", "Belfast", 54.5973, -5.9301, "BT"),
("Brighton", "East Sussex", 50.8225, -0.1372, "BN"),
("Leicester", "Leicestershire", 52.6369, -1.1398, "LE"),
("Aberdeen", "Aberdeen", 57.1497, -2.0943, "AB"),
("Portsmouth", "Hampshire", 50.8198, -1.0880, "PO"),
("York", "North Yorkshire", 53.9599, -1.0873, "YO"),
("Swansea", "Swansea", 51.6214, -3.9436, "SA"),
("Oxford", "Oxfordshire", 51.7520, -1.2577, "OX"),
("Cambridge", "Cambridgeshire", 52.2053, 0.1218, "CB"),
("Exeter", "Devon", 50.7184, -3.5339, "EX"),
("Bath", "Somerset", 51.3751, -2.3617, "BA"),
("Reading", "Berkshire", 51.4543, -0.9781, "RG"),
("Preston", "Lancashire", 53.7632, -2.7031, "PR"),
("Coventry", "West Midlands", 52.4068, -1.5197, "CV"),
("Hull", "East Yorkshire", 53.7676, -0.3274, "HU"),
("Stoke-on-Trent", "Staffordshire", 53.0027, -2.1794, "ST"),
("Wolverhampton", "West Midlands", 52.5870, -2.1288, "WV"),
("Plymouth", "Devon", 50.3755, -4.1427, "PL"),
("Derby", "Derbyshire", 52.9225, -1.4746, "DE"),
("Sunderland", "Tyne and Wear", 54.9069, -1.3830, "SR"),
("Southampton", "Hampshire", 50.9097, -1.4044, "SO"),
("Norwich", "Norfolk", 52.6309, 1.2974, "NR"),
("Bournemouth", "Dorset", 50.7192, -1.8808, "BH"),
("Middlesbrough", "North Yorkshire", 54.5742, -1.2350, "TS"),
("Blackpool", "Lancashire", 53.8175, -3.0357, "FY"),
("Bolton", "Greater Manchester", 53.5785, -2.4299, "BL"),
("Ipswich", "Suffolk", 52.0567, 1.1482, "IP"),
("Telford", "Shropshire", 52.6784, -2.4453, "TF"),
("Dundee", "Dundee", 56.4620, -2.9707, "DD"),
("Peterborough", "Cambridgeshire", 52.5695, -0.2405, "PE"),
("Huddersfield", "West Yorkshire", 53.6458, -1.7850, "HD"),
("Luton", "Bedfordshire", 51.8787, -0.4200, "LU"),
("Warrington", "Cheshire", 53.3900, -2.5970, "WA"),
("Southend-on-Sea", "Essex", 51.5459, 0.7077, "SS"),
("Swindon", "Wiltshire", 51.5557, -1.7797, "SN"),
("Slough", "Berkshire", 51.5105, -0.5950, "SL"),
("Watford", "Hertfordshire", 51.6565, -0.3903, "WD"),
("Carlisle", "Cumbria", 54.8952, -2.9335, "CA")
]
# Street name components for generating realistic street names
street_prefixes = ["High", "Main", "Church", "Park", "Mill", "Station", "London", "Victoria", "Queen", "King", "North", "South", "East", "West", "New", "Old", "Castle", "Bridge", "Green", "Market", "School", "Manor", "Abbey", "Priory", "Cathedral", "University", "College", "Hospital", "Railway", "Canal", "River", "Forest", "Wood", "Hill", "Mount", "Valley", "Meadow", "Field", "Farm", "Garden", "Orchard", "Vineyard", "Grange", "Lodge", "Court", "Hall", "House", "Cottage", "Barn", "Mill", "Windmill", "Watermill", "Forge", "Quarry", "Mine", "Pit", "Well", "Spring", "Brook", "Stream", "Lake", "Pond", "Pool", "Reservoir", "Bay", "Cove", "Beach", "Cliff", "Rock", "Stone", "Granite", "Marble", "Slate", "Clay", "Sand", "Gravel", "Chalk", "Flint", "Coal", "Iron", "Steel", "Copper", "Silver", "Gold", "Tin", "Lead", "Zinc", "Brass", "Bronze", "Pewter", "Nickel", "Cobalt", "Chromium", "Titanium", "Aluminium", "Silicon", "Carbon", "Oxygen", "Hydrogen", "Nitrogen", "Helium", "Neon", "Argon", "Krypton", "Xenon", "Radon"]
street_suffixes = ["Street", "Road", "Lane", "Avenue", "Drive", "Boulevard", "Way", "Place", "Square", "Court", "Terrace", "Close", "Crescent", "Gardens", "Grove", "Mews", "Alley", "Walk", "Path", "Trail", "Hill", "Rise", "View", "Heights", "Park", "Green", "Meadow", "Field", "Common", "Heath", "Moor", "Down", "Fell", "Pike", "Tor", "Crag", "Cliff", "Ridge", "Edge", "Top", "Bottom", "Side", "End", "Corner", "Junction", "Cross", "Gate", "Bridge", "Ford", "Ferry", "Wharf", "Quay", "Dock", "Harbor", "Port", "Bay", "Cove", "Beach", "Shore", "Bank", "Strand", "Esplanade", "Parade", "Promenade", "Embankment", "Causeway", "Viaduct", "Tunnel", "Passage", "Arcade", "Gallery", "Mall", "Market", "Bazaar", "Fair", "Exchange", "Mart", "Emporium", "Center", "Circle", "Oval", "Triangle", "Pentagon", "Hexagon", "Octagon", "Circus", "Ring", "Loop", "Bend", "Curve", "Turn", "Twist", "Spiral", "Coil", "Helix", "Maze", "Labyrinth"]
# Facility descriptions by category
category_descriptions = {
1: [ # Recycling Bins
"Public recycling point for paper, glass, plastic, and metal",
"Community recycling station with separate bins for different materials",
"Recycling center with facilities for household waste separation",
"Public access recycling bins for common household recyclables",
"Multi-material recycling point with clear instructions for proper sorting"
],
2: [ # e-Scooters
"Dockless e-scooter rental station with multiple vehicles available",
"E-scooter parking and charging zone for public use",
"Designated e-scooter pickup and drop-off point",
"E-scooter sharing station with app-based rental system",
"Electric scooter hub with maintenance and charging facilities"
],
3: [ # Bike Share Stations
"Public bicycle sharing station with multiple bikes available",
"Bike rental hub with secure docking stations",
"Community bike share point with regular and electric bicycles",
"Cycle hire station with self-service rental system",
"Bike sharing facility with maintenance and repair services"
],
4: [ # Public EV Charging Stations
"Electric vehicle charging point with multiple connectors",
"Fast-charging station for electric vehicles",
"Public EV charging facility with covered waiting area",
"Multi-vehicle electric charging hub with different power options",
"EV charging station with renewable energy source"
],
5: [ # Battery Recycling Points
"Dedicated collection point for used batteries of all sizes",
"Battery recycling bin with separate compartments for different types",
"Safe disposal facility for household and small electronics batteries",
"Battery collection point with educational information about recycling",
"Secure battery recycling station to prevent environmental contamination"
],
6: [ # Community Compost Bins
"Neighborhood composting facility for food and garden waste",
"Community compost bins with educational signage",
"Public composting station with separate sections for different stages",
"Shared compost facility managed by local volunteers",
"Urban composting hub turning food waste into valuable soil"
],
7: [ # Solar-Powered Benches
"Solar bench with USB charging ports and WiFi connectivity",
"Public seating with integrated solar panels and device charging",
"Smart bench powered by solar energy with digital information display",
"Solar-powered rest area with phone charging capabilities",
"Eco-friendly bench with solar panels and LED lighting"
],
8: [ # Green Roofs
"Building with extensive green roof system visible from public areas",
"Accessible green roof garden with native plant species",
"Public building showcasing sustainable rooftop vegetation",
"Green roof installation with educational tours available",
"Biodiverse roof garden with insect habitats and rainwater collection"
],
9: [ # Public Water Refill Stations
"Free water refill station to reduce plastic bottle usage",
"Public drinking fountain with bottle filling capability",
"Water refill point with filtered water options",
"Accessible water station encouraging reusable bottles",
"Community water dispenser with usage counter display"
],
10: [ # Waste Oil Collection Points
"Cooking oil recycling point for residential use",
"Used oil collection facility with secure containers",
"Waste oil drop-off point for conversion to biodiesel",
"Community oil recycling station with spill prevention measures",
"Cooking oil collection facility with educational information"
],
11: [ # Book Swap Stations
"Community book exchange point with weatherproof shelving",
"Public book sharing library in repurposed phone box",
"Free book swap station encouraging reading and reuse",
"Neighborhood book exchange with rotating collection",
"Little free library with take-one-leave-one system"
],
12: [ # Pollinator Gardens
"Public garden designed to support bees and butterflies",
"Pollinator-friendly planting area with native flowering species",
"Community garden dedicated to supporting local insect populations",
"Bee-friendly garden with educational signage about pollinators",
"Urban wildflower meadow supporting biodiversity"
],
13: [ # E-Waste Collection Bins
"Secure collection point for electronic waste and small appliances",
"E-waste recycling bin for phones, computers, and small electronics",
"Electronic waste drop-off point with data security assurance",
"Community e-waste collection facility with regular collection schedule",
"Dedicated bin for responsible disposal of electronic items"
],
14: [ # Clothing Donation Bins
"Textile recycling point for clothes and household fabrics",
"Clothing donation bin supporting local charities",
"Secure collection point for reusable clothing and textiles",
"Community clothing recycling bin with regular collection",
"Textile donation point preventing landfill waste"
],
15: [ # Community Tool Libraries
"Tool lending library for community use and sharing",
"Shared equipment facility reducing need for individual ownership",
"Community resource center for borrowing tools and equipment",
"Tool sharing hub with membership system and workshops",
"Public tool library with wide range of equipment available"
],
16: [ # Urban Farms
"Community-run urban farm providing local produce",
"City farming project with volunteer opportunities",
"Urban agriculture site with educational programs",
"Local food growing initiative in repurposed urban space",
"Community garden with vegetable plots and fruit trees"
],
17: [ # Rainwater Harvesting Systems
"Public demonstration of rainwater collection for irrigation",
"Rainwater harvesting system with educational displays",
"Community rainwater collection facility for shared gardens",
"Visible rainwater storage and filtration system",
"Urban water conservation project with storage tanks"
]
}
# Status comments by category
status_comments = {
1: [ # Recycling Bins
"Recently emptied and cleaned",
"Some bins are nearly full",
"All bins in good condition",
"Paper bin is currently full",
"New signage installed to improve sorting"
],
2: [ # e-Scooters
"All scooters fully charged",
"Three scooters currently available",
"Maintenance scheduled for next week",
"New scooters added to this location",
"High usage area, scooters frequently unavailable"
],
3: [ # Bike Share Stations
"All docking stations operational",
"Five bikes currently available",
"Some bikes need maintenance",
"New electric bikes added",
"Popular station with high turnover"
],
4: [ # Public EV Charging Stations
"All charging points operational",
"Fast charger currently under repair",
"Peak usage during business hours",
"New charging point added last month",
"Payment system recently upgraded"
],
5: [ # Battery Recycling Points
"Collection bin recently emptied",
"Secure container in good condition",
"New signage explaining battery types",
"High usage from local businesses",
"Additional capacity added"
],
6: [ # Community Compost Bins
"Compost ready for collection",
"Needs more brown material",
"Recently turned and aerated",
"New bins added to increase capacity",
"Volunteer day scheduled for maintenance"
],
7: [ # Solar-Powered Benches
"All charging ports working",
"Solar panels recently cleaned",
"WiFi currently unavailable",
"LED lights need replacement",
"High usage during lunch hours"
],
8: [ # Green Roofs
"Plants thriving after recent rain",
"Maintenance scheduled next month",
"New species added to increase biodiversity",
"Irrigation system working well",
"Open for public tours on weekends"
],
9: [ # Public Water Refill Stations
"Water quality tested weekly",
"Fountain cleaned daily",
"Bottle filler counter shows high usage",
"New filter installed recently",
"Popular during summer months"
],
10: [ # Waste Oil Collection Points
"Container recently emptied",
"Secure lid in good condition",
"New funnel system installed",
"Collection schedule posted",
"Area kept clean and tidy"
],
11: [ # Book Swap Stations
"Good selection currently available",
"Children's books needed",
"Recently reorganized by volunteers",
"Weatherproof cover working well",
"High turnover of popular titles"
],
12: [ # Pollinator Gardens
"Plants in full bloom",
"Many bees and butterflies observed",
"New native species planted",
"Volunteer day for maintenance scheduled",
"Educational tours available"
],
13: [ # E-Waste Collection Bins
"Bin recently emptied",
"Secure deposit system working",
"Collection schedule posted",
"New items accepted now include small appliances",
"Data destruction guaranteed"
],
14: [ # Clothing Donation Bins
"Bin recently emptied",
"Clean and well-maintained",
"High quality donations appreciated",
"Winter clothing especially needed",
"Please bag items before donating"
],
15: [ # Community Tool Libraries
"New inventory system implemented",
"Popular tools often unavailable on weekends",
"Tool maintenance workshop scheduled",
"New donations recently added to collection",
"Extended hours during summer"
],
16: [ # Urban Farms
"Seasonal produce currently available",
"Volunteer opportunities posted",
"Educational workshops on weekends",
"New growing area being developed",
"Composting system recently expanded"
],
17: [ # Rainwater Harvesting Systems
"System working efficiently after recent rainfall",
"Water quality monitoring in place",
"Educational tours available by appointment",
"System capacity recently expanded",
"Used for irrigation of nearby community garden"
]
}
# Generate a realistic UK postcode based on area code
def generate_postcode(area_code):
# Format: Area + District + Space + Sector + Unit
# e.g., M1 1AA or SW1A 1AA
district = random.randint(1, 99)
sector = random.randint(1, 9)
unit = ''.join(random.choices('ABCDEFGHJKLMNPQRSTUVWXYZ', k=2)) # Excluding I and O as they're not used
if len(area_code) == 1:
return f"{area_code}{district} {sector}{unit}"
else:
return f"{area_code}{district} {sector}{unit}"
# Generate a realistic street name
def generate_street_name():
prefix = random.choice(street_prefixes)
suffix = random.choice(street_suffixes)
return f"{prefix} {suffix}"
# Generate a realistic house number
def generate_house_number():
# 80% chance of a simple number, 20% chance of a letter suffix or unit
if random.random() < 0.8:
return str(random.randint(1, 200))
else:
options = [
f"{random.randint(1, 200)}{random.choice('ABCDEFG')}", # e.g., 42A
f"Unit {random.randint(1, 20)}",
f"Flat {random.randint(1, 50)}",
f"Suite {random.randint(1, 10)}"
]
return random.choice(options)
# Add small random variation to coordinates to avoid facilities at exact same location
def vary_coordinates(lat, lng):
# Add variation of up to ~500 meters
lat_variation = random.uniform(-0.004, 0.004)
lng_variation = random.uniform(-0.006, 0.006)
return lat + lat_variation, lng + lng_variation
# Generate facility title based on category and location
def generate_title(category_name, location_name, street_name):
templates = [
f"{location_name} {category_name}",
f"{category_name} at {street_name}",
f"{street_name} {category_name}",
f"Community {category_name} {location_name}",
f"{location_name} Central {category_name}",
f"{location_name} {street_name} {category_name}"
]
return random.choice(templates)
# Create a log file to track progress
log_file = open("facility_generation_log.txt", "w")
log_file.write(f"Starting facility generation at {datetime.now()}\n")
log_file.write(f"Target: 1000 new facilities\n\n")
# Create a CSV file to store all generated facilities for reference
csv_file = open("generated_facilities.csv", "w", newline='')
csv_writer = csv.writer(csv_file)
csv_writer.writerow(["ID", "Title", "Category", "Description", "Address", "Postcode", "Latitude", "Longitude", "Contributor"])
# Prepare for batch insertion to improve performance
facilities_to_insert = []
status_comments_to_insert = []
# Track unique titles to avoid duplicates
existing_titles = set()
cursor.execute("SELECT title FROM ecoFacilities")
for row in cursor.fetchall():
existing_titles.add(row[0])
# Generate 1000 facilities
num_facilities = 1000
facilities_created = 0
log_file.write("Generating facilities...\n")
while facilities_created < num_facilities:
# Select a random location
location = random.choice(uk_locations)
location_name, county, base_lat, base_lng, postcode_area = location
# Generate 5-25 facilities per location to create clusters
facilities_per_location = min(random.randint(5, 25), num_facilities - facilities_created)
for _ in range(facilities_per_location):
# Select a random category
category_id = random.choice(list(categories.keys()))
category_name = categories[category_id]
# Generate address components
street_name = generate_street_name()
house_number = generate_house_number()
lat, lng = vary_coordinates(base_lat, base_lng)
postcode = generate_postcode(postcode_area)
# Generate title
title_base = generate_title(category_name, location_name, street_name)
title = title_base
# Ensure title is unique by adding a suffix if needed
suffix = 2
while title in existing_titles:
title = f"{title_base} {suffix}"
suffix += 1
existing_titles.add(title)
# Select description
description = random.choice(category_descriptions[category_id])
# Select contributor
contributor_id = random.choice(user_ids)
# Add to batch for insertion
facilities_to_insert.append((
title,
category_id,
description,
house_number,
street_name,
county,
location_name,
postcode,
lng,
lat,
contributor_id
))
# Log progress periodically
facilities_created += 1
if facilities_created % 100 == 0:
log_message = f"Generated {facilities_created} facilities so far..."
print(log_message)
log_file.write(log_message + "\n")
if facilities_created >= num_facilities:
break
# Insert facilities in batches for better performance
log_file.write("\nInserting facilities into database...\n")
print("Inserting facilities into database...")
batch_size = 50
for i in range(0, len(facilities_to_insert), batch_size):
batch = facilities_to_insert[i:i+batch_size]
cursor.executemany("""
INSERT INTO ecoFacilities
(title, category, description, houseNumber, streetName, county, town, postcode, lng, lat, contributor)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", batch)
# Get the IDs of the inserted facilities
cursor.execute("SELECT last_insert_rowid()")
last_id = cursor.fetchone()[0]
first_id_in_batch = last_id - len(batch) + 1
# Generate status comments for each facility
for j, facility in enumerate(batch):
facility_id = first_id_in_batch + j
category_id = facility[1] # Category ID is the second element
# Write to CSV for reference
csv_writer.writerow([
facility_id,
facility[0], # title
categories[category_id], # category name
facility[2], # description
f"{facility[3]} {facility[4]}, {facility[6]}, {facility[5]}", # address
facility[7], # postcode
facility[9], # lat
facility[8], # lng
facility[10] # contributor
])
# Decide how many status comments to add (0-3)
num_comments = random.choices([0, 1, 2, 3], weights=[30, 40, 20, 10])[0]
if num_comments > 0:
# Get relevant comments for this category
relevant_comments = status_comments.get(category_id, status_comments[1]) # Default to recycling bin comments
# Select random comments without repetition
selected_comments = random.sample(relevant_comments, min(num_comments, len(relevant_comments)))
# Add to batch for insertion
for comment in selected_comments:
status_comments_to_insert.append((facility_id, comment))
# Commit after each batch
conn.commit()
log_message = f"Inserted batch {i//batch_size + 1}/{(len(facilities_to_insert)-1)//batch_size + 1}"
print(log_message)
log_file.write(log_message + "\n")
# Insert status comments in batches
if status_comments_to_insert:
log_file.write("\nInserting status comments...\n")
print("Inserting status comments...")
for i in range(0, len(status_comments_to_insert), batch_size):
batch = status_comments_to_insert[i:i+batch_size]
cursor.executemany("""
INSERT INTO ecoFacilityStatus (facilityId, statusComment)
VALUES (?, ?)
""", batch)
conn.commit()
log_message = f"Inserted comment batch {i//batch_size + 1}/{(len(status_comments_to_insert)-1)//batch_size + 1}"
print(log_message)
log_file.write(log_message + "\n")
# Get final counts
cursor.execute("SELECT COUNT(*) FROM ecoFacilities")
total_facilities = cursor.fetchone()[0]
cursor.execute("SELECT COUNT(*) FROM ecoFacilityStatus")
total_comments = cursor.fetchone()[0]
# Log completion
completion_message = f"\nGeneration complete at {datetime.now()}"
print(completion_message)
log_file.write(completion_message + "\n")
summary = f"Total facilities in database: {total_facilities}\n"
summary += f"Total status comments in database: {total_comments}\n"
summary += f"Generated facilities saved to generated_facilities.csv for reference"
print(summary)
log_file.write(summary)
# Close connections
log_file.close()
csv_file.close()
conn.commit()
conn.close()
print("\nSuccessfully added 1000 new ecological facilities to the database.")
print("A detailed log and CSV export have been created for reference.")

View File

@@ -1,79 +0,0 @@
<?php
// Connect to the SQLite database
$db = new PDO('sqlite:Databases/ecobuddy.sqlite');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// List of real first names for usernames
$firstNames = [
'James', 'John', 'Robert', 'Michael', 'William', 'David', 'Richard', 'Joseph', 'Thomas', 'Charles',
'Christopher', 'Daniel', 'Matthew', 'Anthony', 'Mark', 'Donald', 'Steven', 'Paul', 'Andrew', 'Joshua',
'Kenneth', 'Kevin', 'Brian', 'George', 'Timothy', 'Ronald', 'Edward', 'Jason', 'Jeffrey', 'Ryan',
'Jacob', 'Gary', 'Nicholas', 'Eric', 'Jonathan', 'Stephen', 'Larry', 'Justin', 'Scott', 'Brandon',
'Benjamin', 'Samuel', 'Gregory', 'Alexander', 'Frank', 'Patrick', 'Raymond', 'Jack', 'Dennis', 'Jerry',
'Tyler', 'Aaron', 'Jose', 'Adam', 'Nathan', 'Henry', 'Douglas', 'Zachary', 'Peter', 'Kyle',
'Ethan', 'Walter', 'Noah', 'Jeremy', 'Christian', 'Keith', 'Roger', 'Terry', 'Gerald', 'Harold',
'Sean', 'Austin', 'Carl', 'Arthur', 'Lawrence', 'Dylan', 'Jesse', 'Jordan', 'Bryan', 'Billy',
'Joe', 'Bruce', 'Gabriel', 'Logan', 'Albert', 'Willie', 'Alan', 'Juan', 'Wayne', 'Elijah',
'Randy', 'Roy', 'Vincent', 'Ralph', 'Eugene', 'Russell', 'Bobby', 'Mason', 'Philip', 'Louis'
];
// List of common words for password generation
$words = [
'Apple', 'Banana', 'Cherry', 'Dragon', 'Eagle', 'Forest', 'Garden', 'Harbor', 'Island', 'Jungle',
'Kingdom', 'Lemon', 'Mountain', 'Nature', 'Ocean', 'Planet', 'Queen', 'River', 'Summer', 'Tiger',
'Universe', 'Volcano', 'Winter', 'Yellow', 'Zebra', 'Castle', 'Diamond', 'Emerald', 'Flower', 'Galaxy',
'Horizon', 'Iceberg', 'Journey', 'Knight', 'Legend', 'Meadow', 'Nebula', 'Oasis', 'Palace', 'Quasar',
'Rainbow', 'Sapphire', 'Thunder', 'Unicorn', 'Victory', 'Whisper', 'Xylophone', 'Yacht', 'Zephyr', 'Autumn',
'Breeze', 'Cascade', 'Dolphin', 'Eclipse', 'Falcon', 'Glacier', 'Harmony', 'Infinity', 'Jasmine', 'Kaleidoscope',
'Lighthouse', 'Mirage', 'Nightfall', 'Orchard', 'Phoenix', 'Quicksilver', 'Radiance', 'Serenity', 'Twilight', 'Umbrella'
];
// Check if we already have users with these names
$existingUsers = [];
$stmt = $db->query("SELECT username FROM ecoUser");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$existingUsers[] = $row['username'];
}
// Prepare the SQL statement for inserting users
$insertStmt = $db->prepare("INSERT INTO ecoUser (username, password, userType) VALUES (?, ?, ?)");
// Create a file to store usernames and passwords
$file = fopen("user_credentials.txt", "w");
fwrite($file, "Username,Password\n");
// Generate 50 users
$usersAdded = 0;
$usedNames = [];
shuffle($firstNames);
foreach ($firstNames as $name) {
if ($usersAdded >= 50) break;
// Skip if the name is already in the database
if (in_array($name, $existingUsers) || in_array($name, $usedNames)) {
continue;
}
// Generate password in format (Word)(Word)(Word)(Digit)
$password = $words[array_rand($words)] . $words[array_rand($words)] . $words[array_rand($words)] . rand(0, 9);
// Hash the password using SHA-256
$hashedPassword = hash('sha256', $password);
// Insert the user into the database (userType 2 is for standard users)
$insertStmt->execute([$name, $hashedPassword, 2]);
// Write the credentials to the file
fwrite($file, "$name,$password\n");
$usedNames[] = $name;
$usersAdded++;
echo "Added user: $name\n";
}
fclose($file);
echo "\nSuccessfully added $usersAdded users to the database.\n";
echo "Usernames and passwords have been saved to user_credentials.txt\n";

View File

@@ -1,79 +0,0 @@
import sqlite3
import random
import hashlib
import os
# Connect to the SQLite database
conn = sqlite3.connect('Databases/ecobuddy.sqlite')
cursor = conn.cursor()
# List of real first names for usernames
first_names = [
'James', 'John', 'Robert', 'Michael', 'William', 'David', 'Richard', 'Joseph', 'Thomas', 'Charles',
'Christopher', 'Daniel', 'Matthew', 'Anthony', 'Mark', 'Donald', 'Steven', 'Paul', 'Andrew', 'Joshua',
'Kenneth', 'Kevin', 'Brian', 'George', 'Timothy', 'Ronald', 'Edward', 'Jason', 'Jeffrey', 'Ryan',
'Jacob', 'Gary', 'Nicholas', 'Eric', 'Jonathan', 'Stephen', 'Larry', 'Justin', 'Scott', 'Brandon',
'Benjamin', 'Samuel', 'Gregory', 'Alexander', 'Frank', 'Patrick', 'Raymond', 'Jack', 'Dennis', 'Jerry',
'Tyler', 'Aaron', 'Jose', 'Adam', 'Nathan', 'Henry', 'Douglas', 'Zachary', 'Peter', 'Kyle',
'Ethan', 'Walter', 'Noah', 'Jeremy', 'Christian', 'Keith', 'Roger', 'Terry', 'Gerald', 'Harold',
'Sean', 'Austin', 'Carl', 'Arthur', 'Lawrence', 'Dylan', 'Jesse', 'Jordan', 'Bryan', 'Billy',
'Joe', 'Bruce', 'Gabriel', 'Logan', 'Albert', 'Willie', 'Alan', 'Juan', 'Wayne', 'Elijah',
'Randy', 'Roy', 'Vincent', 'Ralph', 'Eugene', 'Russell', 'Bobby', 'Mason', 'Philip', 'Louis'
]
# List of common words for password generation
words = [
'Apple', 'Banana', 'Cherry', 'Dragon', 'Eagle', 'Forest', 'Garden', 'Harbor', 'Island', 'Jungle',
'Kingdom', 'Lemon', 'Mountain', 'Nature', 'Ocean', 'Planet', 'Queen', 'River', 'Summer', 'Tiger',
'Universe', 'Volcano', 'Winter', 'Yellow', 'Zebra', 'Castle', 'Diamond', 'Emerald', 'Flower', 'Galaxy',
'Horizon', 'Iceberg', 'Journey', 'Knight', 'Legend', 'Meadow', 'Nebula', 'Oasis', 'Palace', 'Quasar',
'Rainbow', 'Sapphire', 'Thunder', 'Unicorn', 'Victory', 'Whisper', 'Xylophone', 'Yacht', 'Zephyr', 'Autumn',
'Breeze', 'Cascade', 'Dolphin', 'Eclipse', 'Falcon', 'Glacier', 'Harmony', 'Infinity', 'Jasmine', 'Kaleidoscope',
'Lighthouse', 'Mirage', 'Nightfall', 'Orchard', 'Phoenix', 'Quicksilver', 'Radiance', 'Serenity', 'Twilight', 'Umbrella'
]
# Check if we already have users with these names
cursor.execute("SELECT username FROM ecoUser")
existing_users = [row[0] for row in cursor.fetchall()]
# Create a file to store usernames and passwords
with open("user_credentials.txt", "w") as file:
file.write("Username,Password\n")
# Generate 50 users
users_added = 0
used_names = []
random.shuffle(first_names)
for name in first_names:
if users_added >= 50:
break
# Skip if the name is already in the database
if name in existing_users or name in used_names:
continue
# Generate password in format (Word)(Word)(Word)(Digit)
password = random.choice(words) + random.choice(words) + random.choice(words) + str(random.randint(0, 9))
# Hash the password using SHA-256
hashed_password = hashlib.sha256(password.encode()).hexdigest()
# Insert the user into the database (userType 2 is for standard users)
cursor.execute("INSERT INTO ecoUser (username, password, userType) VALUES (?, ?, ?)",
(name, hashed_password, 2))
# Write the credentials to the file
file.write(f"{name},{password}\n")
used_names.append(name)
users_added += 1
print(f"Added user: {name}")
# Commit the changes and close the connection
conn.commit()
conn.close()
print(f"\nSuccessfully added {users_added} users to the database.")
print("Usernames and passwords have been saved to user_credentials.txt")

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,9 @@
require_once('UserDataSet.php');
/**
* Authentication service for handling JWT-based authentication
* Backend Authentication service for handling JWT authentication
* https://jwt.io/introduction
* This cost me blood, sweat and tears, mostly tears.
*/
class AuthService {
private string $secretKey;
@@ -14,7 +16,7 @@ class AuthService {
* @throws Exception if OpenSSL extension is not loaded
*/
public function __construct() {
// Load environment variables from .env file
// Load environment variables from .env file (:D more configuration needs to be added to .env, but scope creep already huge)
$envFile = __DIR__ . '/../.env';
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
@@ -37,14 +39,14 @@ class AuthService {
$this->secretKey = getenv('JWT_SECRET_KEY') ?: 'your-256-bit-secret';
$this->tokenExpiry = (int)(getenv('JWT_TOKEN_EXPIRY') ?: 3600);
// Verify OpenSSL extension is available
// Verify OpenSSL extension is available. This should be on by default regardless, but just in case.
if (!extension_loaded('openssl')) {
throw new Exception('OpenSSL extension is required for JWT');
}
}
/**
* Generates a JWT token for a user
* Generates a JWT token
* @param array $userData User information to include in token
* @return string The generated JWT token
*/
@@ -52,6 +54,7 @@ class AuthService {
$issuedAt = time();
$expire = $issuedAt + $this->tokenExpiry;
// Create payload with user data
$payload = [
'iat' => $issuedAt,
'exp' => $expire,
@@ -101,7 +104,7 @@ class AuthService {
$signature = hash_hmac('sha256', "$header.$payload", $this->secretKey, true);
$signature = $this->base64UrlEncode($signature);
return "$header.$payload.$signature";
return "$header.$payload.$signature"; //Wooooooo!!! JWT is a thing!
}
/**

View File

@@ -1,15 +1,11 @@
<?php
/**
* Represents a facility in the EcoBuddy system
* Represents a singular facility
*
* This class serves as a data model for facilities, encapsulating all
* the properties and behaviours of a single facility. It follows the
* Data Transfer Object (DTO) pattern that I learned about in my
* software architecture module.
* Data model for facilities, encapsulating all
* properties and behaviours of a single facility.
*
* Each facility has location data, descriptive information, and metadata
* about who contributed it. This class provides a clean interface for
* accessing this data throughout the application.
* Each facility has location data, descriptive info, and metadata.
*/
class FacilityData {
/**
@@ -77,10 +73,6 @@ class FacilityData {
/**
* Gets the facility's title
*
* The title is the primary name or label for the facility that
* is displayed to users in the interface.
*
* @return string The facility title
*/
public function getTitle() {
@@ -89,10 +81,6 @@ class FacilityData {
/**
* Gets the facility's category
*
* The category helps classify facilities by type, such as
* recycling centre, community garden, etc.
*
* @return string The facility category
*/
public function getCategory() {
@@ -101,10 +89,6 @@ class FacilityData {
/**
* Gets the facility's current status
*
* The status indicates whether the facility is operational,
* under maintenance, closed, etc.
*
* @return string The facility status
*/
public function getStatus() {
@@ -113,10 +97,6 @@ class FacilityData {
/**
* Gets the facility's description
*
* The description provides detailed information about the facility,
* its purpose, services offered, etc.
*
* @return string The facility description
*/
public function getDescription() {
@@ -125,9 +105,6 @@ class FacilityData {
/**
* Gets the facility's house/building number
*
* This is part of the facility's address and helps locate it physically.
*
* @return string The house/building number
*/
public function getHouseNumber() {
@@ -136,9 +113,6 @@ class FacilityData {
/**
* Gets the facility's street name
*
* This is part of the facility's address and helps locate it physically.
*
* @return string The street name
*/
public function getStreetName() {
@@ -147,9 +121,6 @@ class FacilityData {
/**
* Gets the facility's county
*
* This is part of the facility's address and helps locate it physically.
*
* @return string The county
*/
public function getCounty() {
@@ -158,9 +129,6 @@ class FacilityData {
/**
* Gets the facility's town or city
*
* This is part of the facility's address and helps locate it physically.
*
* @return string The town or city
*/
public function getTown() {
@@ -169,10 +137,6 @@ class FacilityData {
/**
* Gets the facility's postcode
*
* This is part of the facility's address and helps locate it physically.
* It's also useful for searching facilities by location.
*
* @return string The postcode
*/
public function getPostcode() {
@@ -181,10 +145,6 @@ class FacilityData {
/**
* Gets the facility's longitude coordinate
*
* This is used for displaying the facility on a map and
* for calculating distances between facilities.
*
* @return float The longitude coordinate
*/
public function getLng() {
@@ -193,10 +153,6 @@ class FacilityData {
/**
* Gets the facility's latitude coordinate
*
* This is used for displaying the facility on a map and
* for calculating distances between facilities.
*
* @return float The latitude coordinate
*/
public function getLat() {
@@ -205,10 +161,6 @@ class FacilityData {
/**
* Gets the username of the facility's contributor
*
* This tracks who added the facility to the system,
* which is useful for auditing and attribution.
*
* @return string The contributor's username
*/
public function getContributor() {

View File

@@ -1,57 +0,0 @@
<?php
require_once('FacilityDataSet.php');
class Paginator {
protected $_pages, $_totalPages, $_rowLimit, $_pageMatrix, $_rowCount;
public function __construct($rowLimit, $dataset) {
$this->_rowLimit = $rowLimit;
$this->_totalPages = $this->calculateTotalPages($dataset['count']);
$this->_rowCount = $dataset['count'];
$this->_pages = $dataset['dataset'];
$this->_pageMatrix = $this->Paginate();
}
public function getTotalPages() {
return $this->_totalPages;
}
private function calculateTotalPages(int $count): int {
return $count > 0 ? ceil($count / $this->_rowLimit) : 0;
}
public function Paginate(): array {
$pageMatrix = [];
for ($i = 0; $i < $this->_totalPages; $i++) {
$page = [];
$start = $i * $this->_rowLimit;
$end = min($start + $this->_rowLimit, $this->_rowCount); // Ensure within bounds
for ($j = $start; $j < $end; $j++) {
$page[] = $this->_pages[$j];
}
$pageMatrix[$i] = $page;
}
return $pageMatrix;
}
public function getPageFromUri(): int {
// Retrieve 'page' parameter and default to 0 if missing or invalid
return filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT, [
'options' => ['default' => 0, 'min_range' => 0] // Default to 1 if invalid or missing
]);
}
public function getPage(int $pageNumber): array {
if ($pageNumber < 0 || $pageNumber >= $this->_totalPages) {
return []; // Return an empty array if the page number is invalid
}
return $this->_pageMatrix[$pageNumber];
}
public function countPageResults(int $pageNumber): int {
if ($pageNumber < 0 || $pageNumber >= $this->_totalPages) {
return 0; // Return 0 if the page number is invalid
}
return count($this->_pageMatrix[$pageNumber]);
}
}

View File

@@ -70,16 +70,6 @@ class User {
}
}
/**
* Sets the user's access level
*
* @param int $level The access level to set (admin = 1, regular user = 2)
* @return void
*/
private function setAccessLevel($level) {
$this->_accessLevel = $level;
}
/**
* Gets the user's access level
*
@@ -128,33 +118,6 @@ class User {
return false;
}
}
/**
* Logs the user out
*
* Resets all user properties to their default values.
* Note: This doesn't invalidate the JWT token - handled client-side
* by removing the token from storage.
*
* @return void
*/
public function logout() {
// Reset user properties
$this->_loggedIn = false;
$this->_username = "None";
$this->_userId = "0";
$this->_accessLevel = null;
}
/**
* Checks if the user is currently logged in
*
* @return bool True if the user is logged in, false otherwise
*/
public function isLoggedIn(): bool
{
return $this->_loggedIn;
}
/**
* Static method to check if a request is authenticated

View File

@@ -112,7 +112,7 @@
<input type="text" class="form-control" id="postcode" name="postcode"
placeholder="e.g. M1 5GD" required>
<button class="btn btn-success" type="submit">
<i class="bi bi-search me-1"></i>Search
<i class="bi bi-crosshair"></i>
</button>
</div>
</div>
@@ -151,7 +151,8 @@
<div class="text-center">
<i class="bi bi-geo-alt text-success" style="font-size: 3rem;"></i>
<h4 class="mt-3">Enter a Postcode</h4>
<p class="text-muted">Please enter a postcode to view facilities on the map</p>
<p class="text-muted mb-0">Please enter a postcode to view facilities on the map</p>
<p class="text-muted mt-0">or use the search button to find facilities near you</p>
</div>
</div>
</div>

View File

@@ -159,11 +159,6 @@
<i class="bi bi-map-fill me-1"></i>Map
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/about.php">
<i class="bi bi-info-circle-fill me-1"></i>About
</a>
</li>
</ul>

View File

@@ -1,225 +0,0 @@
import sqlite3
import random
# Connect to the SQLite database
conn = sqlite3.connect('Databases/ecobuddy.sqlite')
cursor = conn.cursor()
# Check if we need to add any new categories
cursor.execute("SELECT id FROM ecoCategories")
existing_categories = [row[0] for row in cursor.fetchall()]
# Add two new categories
new_categories = [
(16, "Urban Farms"),
(17, "Rainwater Harvesting Systems")
]
for category in new_categories:
if category[0] not in existing_categories:
cursor.execute("INSERT INTO ecoCategories (id, name) VALUES (?, ?)", category)
print(f"Added new category: {category[1]}")
# Get list of user IDs for contributors
cursor.execute("SELECT id FROM ecoUser")
user_ids = [row[0] for row in cursor.fetchall()]
# Define 10 new ecological facilities in the UK with accurate location data
new_facilities = [
{
"title": "Community Garden Hackney",
"category": 12, # Pollinator Gardens
"description": "Urban garden with native plants to support local pollinators",
"houseNumber": "45",
"streetName": "Dalston Lane",
"county": "Greater London",
"town": "London",
"postcode": "E8 3AH",
"lng": -0.0612,
"lat": 51.5476,
"contributor": random.choice(user_ids),
"status_comments": [
"Recently expanded with new wildflower section",
"Volunteer days every Saturday"
]
},
{
"title": "Rooftop Solar Farm",
"category": 8, # Green Roofs
"description": "Combined green roof and solar panel installation on commercial building",
"houseNumber": "120",
"streetName": "Deansgate",
"county": "Greater Manchester",
"town": "Manchester",
"postcode": "M3 2QJ",
"lng": -2.2484,
"lat": 53.4808,
"contributor": random.choice(user_ids),
"status_comments": [
"Generates power for the entire building"
]
},
{
"title": "Edinburgh Tool Library",
"category": 15, # Community Tool Libraries
"description": "Borrow tools instead of buying them - reducing waste and consumption",
"houseNumber": "25",
"streetName": "Leith Walk",
"county": "Edinburgh",
"town": "Edinburgh",
"postcode": "EH6 8LN",
"lng": -3.1752,
"lat": 55.9677,
"contributor": random.choice(user_ids),
"status_comments": []
},
{
"title": "Cardiff Bay Water Refill Station",
"category": 9, # Public Water Refill Stations
"description": "Free water refill station to reduce plastic bottle usage",
"houseNumber": "3",
"streetName": "Mermaid Quay",
"county": "Cardiff",
"town": "Cardiff",
"postcode": "CF10 5BZ",
"lng": -3.1644,
"lat": 51.4644,
"contributor": random.choice(user_ids),
"status_comments": [
"Recently cleaned and maintained",
"High usage during summer months"
]
},
{
"title": "Bristol Urban Farm",
"category": 16, # Urban Farms (new category)
"description": "Community-run urban farm providing local produce and education",
"houseNumber": "18",
"streetName": "Stapleton Road",
"county": "Bristol",
"town": "Bristol",
"postcode": "BS5 0RA",
"lng": -2.5677,
"lat": 51.4635,
"contributor": random.choice(user_ids),
"status_comments": [
"Open for volunteers Tuesday-Sunday"
]
},
{
"title": "Newcastle Rainwater Collection System",
"category": 17, # Rainwater Harvesting Systems (new category)
"description": "Public demonstration of rainwater harvesting for garden irrigation",
"houseNumber": "55",
"streetName": "Northumberland Street",
"county": "Tyne and Wear",
"town": "Newcastle upon Tyne",
"postcode": "NE1 7DH",
"lng": -1.6178,
"lat": 54.9783,
"contributor": random.choice(user_ids),
"status_comments": []
},
{
"title": "Brighton Beach Solar Bench",
"category": 7, # Solar-Powered Benches
"description": "Solar-powered bench with USB charging ports and WiFi",
"houseNumber": "",
"streetName": "Kings Road",
"county": "East Sussex",
"town": "Brighton",
"postcode": "BN1 2FN",
"lng": -0.1426,
"lat": 50.8214,
"contributor": random.choice(user_ids),
"status_comments": [
"Popular spot for tourists",
"One USB port currently not working"
]
},
{
"title": "Leeds Community Compost Hub",
"category": 6, # Community Compost Bins
"description": "Large-scale community composting facility for local residents",
"houseNumber": "78",
"streetName": "Woodhouse Lane",
"county": "West Yorkshire",
"town": "Leeds",
"postcode": "LS2 9JT",
"lng": -1.5491,
"lat": 53.8067,
"contributor": random.choice(user_ids),
"status_comments": [
"Recently expanded capacity"
]
},
{
"title": "Glasgow EV Charging Hub",
"category": 4, # Public EV Charging Stations
"description": "Multi-vehicle EV charging station with fast chargers",
"houseNumber": "42",
"streetName": "Buchanan Street",
"county": "Glasgow",
"town": "Glasgow",
"postcode": "G1 3JX",
"lng": -4.2526,
"lat": 55.8621,
"contributor": random.choice(user_ids),
"status_comments": [
"6 charging points available",
"24/7 access"
]
},
{
"title": "Oxford E-Waste Collection Center",
"category": 13, # E-Waste Collection Bins
"description": "Dedicated facility for proper disposal and recycling of electronic waste",
"houseNumber": "15",
"streetName": "St Aldate's",
"county": "Oxfordshire",
"town": "Oxford",
"postcode": "OX1 1BX",
"lng": -1.2577,
"lat": 51.7520,
"contributor": random.choice(user_ids),
"status_comments": []
}
]
# Insert facilities into the database
for facility in new_facilities:
cursor.execute("""
INSERT INTO ecoFacilities
(title, category, description, houseNumber, streetName, county, town, postcode, lng, lat, contributor)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
facility["title"],
facility["category"],
facility["description"],
facility["houseNumber"],
facility["streetName"],
facility["county"],
facility["town"],
facility["postcode"],
facility["lng"],
facility["lat"],
facility["contributor"]
))
# Get the ID of the inserted facility
facility_id = cursor.lastrowid
# Add status comments if any
for comment in facility["status_comments"]:
cursor.execute("""
INSERT INTO ecoFacilityStatus (facilityId, statusComment)
VALUES (?, ?)
""", (facility_id, comment))
print(f"Added facility: {facility['title']} in {facility['town']}")
# Commit the changes and close the connection
conn.commit()
conn.close()
print("\nSuccessfully added 10 new ecological facilities to the database.")

16988
debug.log

File diff suppressed because it is too large Load Diff

View File

@@ -6,9 +6,8 @@
*
* The client uses JWT tokens for authentication, which are automatically
* included in requests via the fetchAuth function provided by the simpleAuth service.
*
* NOTE: For authentication (login, logout, token validation), please use the simpleAuth
* service directly instead of this API client.
*
* Similar to AuthService.php, great pain and countless tears. And learning woooo!!!!!!!!
*/
class ApiClient {
/**

View File

@@ -28,6 +28,63 @@ document.addEventListener('DOMContentLoaded', function() {
// Get facilities data from sessionStorage
facilities = JSON.parse(sessionStorage.getItem('facilityData') || '[]');
// Add location found handler
map.on('locationfound', function(e) {
try {
const { lat, lng } = e.latlng;
// Update the map directly with the coordinates
updateMapLocation({ lat, lng }, currentRadius);
// Remove overlay once we have a valid location
const overlay = document.getElementById('mapOverlay');
if (overlay) {
overlay.classList.add('hidden');
}
// Get postcode from coordinates
fetch(`https://api.postcodes.io/postcodes?lon=${lng}&lat=${lat}`)
.then(response => response.json())
.then(data => {
if (data.status === 200 && data.result && data.result.length > 0) {
const postcode = data.result[0].postcode;
const postcodeInput = document.getElementById('postcode');
if (postcodeInput) {
postcodeInput.value = postcode;
}
}
})
.catch(error => {
console.error('Error getting postcode:', error);
});
} catch (error) {
console.error('Error processing location:', error);
alert('Error getting your location: ' + error.message);
}
});
// Add location error handler
map.on('locationerror', function(e) {
console.error('Geolocation error:', e);
let message = 'Error getting your location: ';
switch(e.code) {
case 1: // PERMISSION_DENIED
message += 'Please enable location access in your browser settings.';
break;
case 2: // POSITION_UNAVAILABLE
message += 'Location information is unavailable.';
break;
case 3: // TIMEOUT
message += 'Location request timed out.';
break;
default:
message += 'An unknown error occurred.';
}
alert(message);
});
// Set up form handlers
setupFormHandlers();
@@ -35,6 +92,84 @@ document.addEventListener('DOMContentLoaded', function() {
setupHeaderSearchHandler();
});
/**
* Get postcode from coordinates using postcodes.io API
* @param {number} lat - Latitude
* @param {number} lng - Longitude
* @returns {Promise<string>} The postcode
*/
async function getPostcodeFromCoordinates(lat, lng) {
try {
const response = await fetch(`https://api.postcodes.io/postcodes?lon=${lng}&lat=${lat}`);
if (!response.ok) {
throw new Error('Could not find postcode for coordinates');
}
const data = await response.json();
if (data.status === 200 && data.result && data.result.length > 0) {
return data.result[0].postcode;
}
throw new Error('No postcode found for coordinates');
} catch (error) {
console.error('Error getting postcode from coordinates:', error);
throw error;
}
}
/**
* Handle geolocation success
* @param {GeolocationPosition} position - The position object
*/
async function handleGeolocationSuccess(position) {
try {
const { latitude, longitude } = position.coords;
// Get postcode from coordinates
const postcode = await getPostcodeFromCoordinates(latitude, longitude);
// Update the postcode input
const postcodeInput = document.getElementById('postcode');
if (postcodeInput) {
postcodeInput.value = postcode;
// Submit the form to update the map
const postcodeForm = document.getElementById('postcodeForm');
if (postcodeForm) {
postcodeForm.dispatchEvent(new Event('submit'));
}
}
} catch (error) {
console.error('Error processing geolocation:', error);
alert('Error getting your location: ' + error.message);
}
}
/**
* Handle geolocation error
* @param {GeolocationPositionError} error - The error object
*/
function handleGeolocationError(error) {
console.error('Geolocation error:', error);
let message = 'Error getting your location: ';
switch(error.code) {
case error.PERMISSION_DENIED:
message += 'Please enable location access in your browser settings.';
break;
case error.POSITION_UNAVAILABLE:
message += 'Location information is unavailable.';
break;
case error.TIMEOUT:
message += 'Location request timed out.';
break;
default:
message += 'An unknown error occurred.';
}
alert(message);
}
/**
* Set up form handlers for postcode and radius inputs
*/
@@ -43,6 +178,24 @@ function setupFormHandlers() {
const radiusSelect = document.getElementById('radius');
if (postcodeForm) {
// Add geolocation functionality to the search button
const searchButton = postcodeForm.querySelector('button[type="submit"]');
if (searchButton) {
searchButton.onclick = (e) => {
// If the postcode input is empty, use geolocation
const postcodeInput = document.getElementById('postcode');
if (!postcodeInput.value.trim()) {
e.preventDefault();
map.locate({
setView: false,
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
});
}
};
}
postcodeForm.addEventListener('submit', async function(e) {
e.preventDefault();
@@ -51,12 +204,7 @@ function setupFormHandlers() {
// Show loading state
const submitButton = this.querySelector('button[type="submit"]');
const originalButtonContent = `<i class="bi bi-search me-1"></i>Search...`;
submitButton.disabled = true;
submitButton.innerHTML = `
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<span class="ms-2">Searching...</span>
`;
// Validate postcode format first
if (!isValidPostcode(postcode)) {
@@ -92,7 +240,6 @@ function setupFormHandlers() {
} finally {
// Always reset button state
submitButton.disabled = false;
submitButton.innerHTML = originalButtonContent;
}
});
}

View File

@@ -1 +0,0 @@
console.log('Testing admin check:'); console.log('User with accessLevel 1:', simpleAuth.isAdmin.call({isAuthenticated: () => true, user: {accessLevel: 1}})); console.log('User with accessLevel 0:', simpleAuth.isAdmin.call({isAuthenticated: () => true, user: {accessLevel: 0}}));