Skip to content

Commit

Permalink
Merge pull request #33 from utmgdsc/feature/62/energy_metric
Browse files Browse the repository at this point in the history
[62] Energy Metric
  • Loading branch information
Yazan10x authored Nov 28, 2023
2 parents 4d752de + df776c5 commit 2677dc0
Show file tree
Hide file tree
Showing 40 changed files with 1,979 additions and 438 deletions.
25 changes: 20 additions & 5 deletions backend/app.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Python Imports
from flask import Flask, Response, jsonify, render_template
from flask import Flask, Response, jsonify
from flask_cors import CORS
from werkzeug.exceptions import HTTPException

from routes.ct_firebase_service import ct_firebase_service
# Imports
from routes.users import users
from routes.transportation import transportation_service
from utils.customJSONEncoder import CustomJSONProvider
from routes.food import food_service
from routes.energy import energy_service


app = Flask(__name__)
Expand All @@ -16,6 +19,8 @@
app.register_blueprint(users, url_prefix="/users")
app.register_blueprint(transportation_service, url_prefix="/transportation")
app.register_blueprint(food_service, url_prefix="/food")
app.register_blueprint(energy_service, url_prefix="/energy")
app.register_blueprint(ct_firebase_service, url_prefix="/f")
CORS(app)


Expand All @@ -24,10 +29,20 @@ def home() -> Response:
return jsonify('Carbon Track APP BACKEND API :: UNAUTHORIZED ACCESS')


# This is just for testing
@app.route("/google")
def test_google() -> str:
return render_template('index.html')
# Error handler for 400 Bad Request errors
@app.errorhandler(400)
def bad_request(error: HTTPException):
response = jsonify({"error": "Bad Request", "message": error.description})
response.status_code = error.code
return response


# Error handler for 404 Not Found errors
@app.errorhandler(404)
def not_found(error: HTTPException):
response = jsonify({"error": error.name, "message": error.description})
response.status_code = error.code
return response


if __name__ == '__main__':
Expand Down
81 changes: 81 additions & 0 deletions backend/models/abstract_carbon_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
Carbon Model
All classes that store carbon usage must inherit from this abstract class
"""
from __future__ import annotations
import json
from datetime import datetime, timezone
from typing import Union

from bson import ObjectId
from models.abstract_db_model import DB_MODEL


class CARBON_MODEL(DB_MODEL):

oid: ObjectId
user_id: ObjectId
carbon_emissions: int
date: datetime

def __init__(self, oid: ObjectId, user_id: ObjectId, carbon_emissions: int, date: Union[str, datetime]) -> None:
super().__init__(oid)
self.user_id = ObjectId(user_id)
self.carbon_emissions = carbon_emissions
if isinstance(date, datetime):
self.date = date
else:
self.date = datetime.fromisoformat(date)

def to_json(self) -> json:
raise NotImplementedError

@staticmethod
def from_json(doc: json) -> CARBON_MODEL:
raise NotImplementedError

def calculate_carbon_emissions(self) -> int:
raise NotImplementedError

@staticmethod
def get_monthly_view(start: datetime, end: datetime,
carbonEntries: list[CARBON_MODEL]) -> list[dict[str, Union[list[float], str]]]:
monthly_data = []

# Make start date offset-aware (assuming UTC for simplicity)
start = start.replace(tzinfo=timezone.utc)

current_month = start
while current_month <= end:
# Add the current month to the list
monthly_data.append({
"month": current_month.strftime("%B"),
"year": current_month.strftime("%Y"),
"data": [0, 0, 0, 0]
})

# Move to the next month
if current_month.month == 12:
current_month = datetime(current_month.year + 1, 1, 1, tzinfo=timezone.utc)
else:
current_month = datetime(current_month.year, current_month.month + 1, 1, tzinfo=timezone.utc)

for carbon_entry in carbonEntries:
for monthly_entry in monthly_data:
if carbon_entry.date.strftime("%B") == monthly_entry["month"] \
and carbon_entry.date.strftime("%Y") == monthly_entry["year"]:
if carbon_entry.date.day < 7:
monthly_entry["data"][0] = carbon_entry.calculate_carbon_emissions()
elif carbon_entry.date.day < 14:
monthly_entry["data"][1] = carbon_entry.calculate_carbon_emissions()
elif carbon_entry.date.day < 21:
monthly_entry["data"][2] = carbon_entry.calculate_carbon_emissions()
elif carbon_entry.date.day < 28:
monthly_entry["data"][3] += carbon_entry.calculate_carbon_emissions()
else: # If a Month has 5 sunday, we add them to the fourth week
monthly_entry["data"][3] += carbon_entry.calculate_carbon_emissions() / 4
return monthly_data

def __repr__(self) -> str:
raise NotImplementedError
2 changes: 1 addition & 1 deletion backend/models/abstract_db_model.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
DB Model
All class that are storable in the db must inherit from this abstract class
All classes that are storable in the db must inherit from this abstract class
"""
from __future__ import annotations
import json
Expand Down
96 changes: 96 additions & 0 deletions backend/models/energy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"""
Energy Model
"""

from __future__ import annotations
from typing import Union
import json
from datetime import datetime
from models.abstract_carbon_model import CARBON_MODEL
from bson import ObjectId


class EnergyEntry(CARBON_MODEL):
oid: ObjectId
user_id: ObjectId
carbon_emissions: int # measured in kgs of CO2
date: datetime
heating_oil: int # measured in L of fuel
natural_gas: int # measured in m3 of gas
electricity: int # measured in kWh
province: str
household: int

def __init__(self, oid: ObjectId, user_id: ObjectId, carbon_emissions: int, date: Union[str, datetime],
heating_oil: int, natural_gas: int, province: str, household: int, electricity: int) -> None:
super().__init__(oid, user_id, carbon_emissions, date)
self.heating_oil = heating_oil
self.natural_gas = natural_gas
self.electricity = electricity
self.province = province
self.household = household

def to_json(self) -> json:
return {
'_id': self.oid,
'user_id': self.user_id,
'heating_oil': self.heating_oil,
'natural_gas': self.natural_gas,
'electricity': self.electricity,
'province': self.province,
'household': self.household,
'carbon_emissions': self.calculate_carbon_emissions(),
'date': self.date
}

@staticmethod
def from_json(doc: json) -> EnergyEntry:
return EnergyEntry(
oid=ObjectId(doc["_id"]),
user_id=doc['user_id'],
heating_oil=doc["heating_oil"],
natural_gas=doc["natural_gas"],
electricity=doc["electricity"],
province=doc["province"],
household=doc["household"],
carbon_emissions=doc["carbon_emissions"],
date=doc["date"]
)

def calculate_carbon_emissions(self) -> float:
heating_oil_carbon_emissions = self.heating_oil * 2.753
natural_gas_carbon_emissions = self.natural_gas * 1.96

if self.household < 1:
self.household = 1

if self.province == "British Columbia":
electricity_carbon_emissions = (self.electricity * 0.015) / self.household
elif self.province == "Alberta":
electricity_carbon_emissions = (self.electricity * 0.54) / self.household
elif self.province == "Saskatchewan":
electricity_carbon_emissions = (self.electricity * 0.73) / self.household
elif self.province == "Manitoba":
electricity_carbon_emissions = (self.electricity * 0.002) / self.household
elif self.province == "Ontario":
electricity_carbon_emissions = (self.electricity * 0.03) / self.household
elif self.province == "Quebec":
electricity_carbon_emissions = (self.electricity * 0.0017) / self.household
elif self.province == "New Brunswick":
electricity_carbon_emissions = (self.electricity * 0.3) / self.household
elif self.province == "Nova Scotia":
electricity_carbon_emissions = (self.electricity * 0.69) / self.household
elif self.province == "PEI":
electricity_carbon_emissions = (self.electricity * 0.3) / self.household
elif self.province == "Newfoundland and Labrador":
electricity_carbon_emissions = (self.electricity * 0.017) / self.household
elif self.province == "Yukon":
electricity_carbon_emissions = (self.electricity * 0.08) / self.household
elif self.province == "Northwest Territories":
electricity_carbon_emissions = (self.electricity * 0.17) / self.household
else: # self.province == "Nunavut"
electricity_carbon_emissions = (self.electricity * 0.84) / self.household
return sum([heating_oil_carbon_emissions, natural_gas_carbon_emissions, electricity_carbon_emissions])

def __repr__(self) -> str:
return f'Energy ID: {self.oid.__str__()}'
36 changes: 15 additions & 21 deletions backend/models/food.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
"""

from __future__ import annotations
from typing import Union
import json
from datetime import datetime
from models.abstract_db_model import DB_MODEL
from models.abstract_carbon_model import CARBON_MODEL
from bson import ObjectId
from bson import json_util


class FoodEntry(DB_MODEL):
class FoodEntry(CARBON_MODEL):
oid: ObjectId
user_id: ObjectId
carbon_emissions: int
date: datetime
beef: int
lamb: int
pork: int
Expand All @@ -21,15 +23,12 @@ class FoodEntry(DB_MODEL):
cheese: int
milk: int
food_waste: int
carbon_emissions: float
date: datetime

# food measurements in # of 100g servings
def __init__(self, oid: ObjectId, user_id: ObjectId, beef: int, lamb: int, pork: int,
chicken: int, fish: int, cheese: int, milk: int, food_waste: int, carbon_emissions: float,
date: datetime) -> None:
super().__init__(oid)
self.user_id = ObjectId(user_id)
def __init__(self, oid: ObjectId, user_id: ObjectId, carbon_emissions: int, date: Union[str, datetime],
beef: int, lamb: int, pork: int, chicken: int, fish: int, cheese: int, milk: int,
food_waste: int) -> None:
super().__init__(oid, user_id, carbon_emissions, date)
self.beef = beef
self.lamb = lamb
self.pork = pork
Expand All @@ -38,27 +37,22 @@ def __init__(self, oid: ObjectId, user_id: ObjectId, beef: int, lamb: int, pork:
self.cheese = cheese
self.milk = milk
self.food_waste = food_waste
self.carbon_emissions = carbon_emissions
self.date = date

def to_json(self, for_mongodb: bool = False) -> json:
res = {
def to_json(self) -> json:
return {
'_id': self.oid,
'user_id': self.user_id,
'beef': self.beef,
'lamb': self.lamb,
'pork': self.pork,
'chicken': self.chicken,
'chicken': self.chicken,
'fish': self.fish,
'cheese': self.cheese,
'cheese': self.cheese,
'milk': self.milk,
'food_waste': self.food_waste,
'food_waste': self.food_waste,
'carbon_emissions': self.calculate_carbon_emissions(),
'date': self.date
}
if for_mongodb:
return res
return json.loads(json_util.dumps(res))

@staticmethod
def from_json(doc: json) -> FoodEntry:
Expand Down Expand Up @@ -87,7 +81,7 @@ def calculate_carbon_emissions(self) -> float:
milk_carbon_emissions = self.milk * 0.8
food_waste_carbon_emissions = self.food_waste * 0.25
return sum([beef_carbon_emissions, lamb_carbon_emissions, pork_carbon_emissions,
chicken_carbon_emissions, fish_carbon_emissions, cheese_carbon_emissions,
chicken_carbon_emissions, fish_carbon_emissions, cheese_carbon_emissions,
milk_carbon_emissions, food_waste_carbon_emissions])

def __repr__(self) -> str:
Expand Down
Loading

0 comments on commit 2677dc0

Please sign in to comment.