Skip to content

Commit

Permalink
issue #158: add fertilizer functions and update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
k-allagbe committed Oct 14, 2024
1 parent a77362e commit eb777a2
Show file tree
Hide file tree
Showing 16 changed files with 1,065 additions and 680 deletions.
12 changes: 11 additions & 1 deletion fertiscan/db/metadata/inspection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from datetime import datetime
from typing import List, Optional

from pydantic import UUID4, BaseModel, ValidationError, model_validator
from pydantic import UUID4, BaseModel, Field, ValidationError, model_validator

from fertiscan.db.queries import (
ingredient,
Expand Down Expand Up @@ -140,6 +140,16 @@ class Inspection(ValidatedModel):
guaranteed_analysis: GuaranteedAnalysis


class Fertilizer(BaseModel):
id: UUID4
name: str | None = None
registration_number: str | None = Field(None, pattern=r"^\d{7}[A-Z]$")
upload_date: datetime
update_at: datetime
latest_inspection_id: UUID4 | None
owner_id: UUID4 | None


def build_inspection_import(analysis_form: dict) -> str:
"""
This funtion build an inspection json object from the pipeline of digitalization analysis.
Expand Down
211 changes: 211 additions & 0 deletions fertiscan/db/queries/fertilizer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
from datetime import datetime
from uuid import UUID

from psycopg import Cursor
from psycopg.rows import dict_row, tuple_row
from psycopg.sql import SQL

# TODO: properly handle exceptions


def create_fertilizer(
cursor: Cursor,
name: str,
registration_number: str | None = None,
latest_inspection_id: str | UUID | None = None,
owner_id: str | UUID | None = None,
):
"""
Inserts a new fertilizer record into the database.
Args:
cursor: Database cursor object.
name: Name of the fertilizer.
registration_number: Optional registration number.
latest_inspection_id: Optional UUID or string of the latest inspection.
owner_id: Optional UUID or string of the owner.
Returns:
The inserted fertilizer record as a dictionary, or None if failed.
"""
query = SQL("""
INSERT INTO fertilizer (name, registration_number, latest_inspection_id, owner_id)
VALUES (%s, %s, %s, %s)
RETURNING *;
""")
with cursor.connection.cursor(row_factory=dict_row) as new_cur:
new_cur.execute(
query, (name, registration_number, latest_inspection_id, owner_id)
)
return new_cur.fetchone()


def read_fertilizer(cursor: Cursor, fertilizer_id: str | UUID):
"""
Retrieves a fertilizer record by ID.
Args:
cursor: Database cursor object.
fertilizer_id: UUID or string ID of the fertilizer.
Returns:
The fertilizer record as a dictionary, or None if not found.
"""
query = SQL("SELECT * FROM fertilizer WHERE id = %s;")
with cursor.connection.cursor(row_factory=dict_row) as new_cur:
new_cur.execute(query, (fertilizer_id,))
return new_cur.fetchone()


def read_all_fertilizers(cursor: Cursor):
"""
Retrieves all fertilizer records from the database.
Args:
cursor: Database cursor object.
Returns:
A list of all fertilizer records as dictionaries.
"""
query = SQL("SELECT * FROM fertilizer;")
with cursor.connection.cursor(row_factory=dict_row) as new_cur:
new_cur.execute(query)
return new_cur.fetchall()


def update_fertilizer(
cursor: Cursor,
fertilizer_id: str | UUID,
name: str | None = None,
registration_number: str | None = None,
latest_inspection_id: str | UUID | None = None,
owner_id: str | UUID | None = None,
):
"""
Updates an existing fertilizer record by ID.
Args:
cursor: Database cursor object.
fertilizer_id: UUID or string ID of the fertilizer.
name: Optional new name of the fertilizer.
registration_number: Optional new registration number.
latest_inspection_id: Optional new inspection ID.
owner_id: Optional new owner ID.
Returns:
The updated fertilizer record as a dictionary, or None if not found.
"""
query = SQL("""
UPDATE fertilizer
SET name = COALESCE(%s, name),
registration_number = COALESCE(%s, registration_number),
latest_inspection_id = COALESCE(%s, latest_inspection_id),
owner_id = COALESCE(%s, owner_id),
update_at = CURRENT_TIMESTAMP
WHERE id = %s
RETURNING *;
""")
with cursor.connection.cursor(row_factory=dict_row) as new_cur:
new_cur.execute(
query,
(name, registration_number, latest_inspection_id, owner_id, fertilizer_id),
)
return new_cur.fetchone()


def upsert_fertilizer(
cursor: Cursor,
name: str,
registration_number: str | None = None,
latest_inspection_id: str | UUID | None = None,
owner_id: str | UUID | None = None,
):
query = SQL("SELECT upsert_fertilizer(%s, %s, %s, %s);")
cursor.row_factory = tuple_row
cursor.execute(query, (name, registration_number, owner_id, latest_inspection_id))
return cursor.fetchone()


def delete_fertilizer(cursor: Cursor, fertilizer_id: str | UUID):
"""
Deletes a fertilizer record by ID.
Args:
cursor: Database cursor object.
fertilizer_id: UUID or string ID of the fertilizer.
Returns:
The deleted fertilizer record as a dictionary, or None if not found.
"""
query = SQL("""
DELETE FROM fertilizer
WHERE id = %s
RETURNING *;
""")
with cursor.connection.cursor(row_factory=dict_row) as new_cur:
new_cur.execute(query, (fertilizer_id,))
return new_cur.fetchone()


def query_fertilizers(
cursor: Cursor,
name: str | None = None,
registration_number: str | None = None,
owner_id: str | UUID | None = None,
latest_inspection_id: str | UUID | None = None,
upload_date_from: str | datetime | None = None,
upload_date_to: str | datetime | None = None,
update_at_from: str | datetime | None = None,
update_at_to: str | datetime | None = None,
) -> list[dict]:
"""
Queries fertilizers based on optional filter criteria.
Args:
cursor: Database cursor object.
name: Optional name to filter fertilizers.
registration_number: Optional registration number.
owner_id: Optional owner UUID (as string or UUID object).
latest_inspection_id: Optional inspection UUID (as string or UUID object).
upload_date_from: Start of the upload date range.
upload_date_to: End of the upload date range.
update_at_from: Start of the update date range.
update_at_to: End of the update date range.
Returns:
A list of fertilizer records matching the filter criteria, as dictionaries.
"""
conditions = []
parameters = []

if name is not None:
conditions.append("name = %s")
parameters.append(name)
if registration_number is not None:
conditions.append("registration_number = %s")
parameters.append(registration_number)
if owner_id is not None:
conditions.append("owner_id = %s")
parameters.append(owner_id)
if latest_inspection_id is not None:
conditions.append("latest_inspection_id = %s")
parameters.append(latest_inspection_id)
if upload_date_from is not None:
conditions.append("upload_date >= %s")
parameters.append(upload_date_from)
if upload_date_to is not None:
conditions.append("upload_date <= %s")
parameters.append(upload_date_to)
if update_at_from is not None:
conditions.append("update_at >= %s")
parameters.append(update_at_from)
if update_at_to is not None:
conditions.append("update_at <= %s")
parameters.append(update_at_to)

where_clause = " WHERE " + " AND ".join(conditions) if conditions else ""
query = SQL(f"SELECT * FROM fertilizer{where_clause};")

with cursor.connection.cursor(row_factory=dict_row) as new_cur:
new_cur.execute(query, parameters)
return new_cur.fetchall()
54 changes: 27 additions & 27 deletions tests/fertiscan/analyse.json
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
{
"company_name":"GreenGrow Fertilizers Inc.",
"company_address":"123 Greenway Blvd, Springfield IL 62701 USA",
"company_website":"www.greengrowfertilizers.com",
"company_phone_number":"+1 800 555 0199",
"manufacturer_name":"AgroTech Industries Ltd.",
"manufacturer_address":"456 Industrial Park Rd, Oakville ON L6H 5V4 Canada",
"manufacturer_website":"www.agrotechindustries.com",
"manufacturer_phone_number":"+1 416 555 0123",
"fertiliser_name":"SuperGrow 20-20-20",
"registration_number":"F12345678",
"lot_number":"L987654321",
"weight":[
"company_name": "GreenGrow Fertilizers Inc.",
"company_address": "123 Greenway Blvd, Springfield IL 62701 USA",
"company_website": "www.greengrowfertilizers.com",
"company_phone_number": "+1 800 555 0199",
"manufacturer_name": "AgroTech Industries Ltd.",
"manufacturer_address": "456 Industrial Park Rd, Oakville ON L6H 5V4 Canada",
"manufacturer_website": "www.agrotechindustries.com",
"manufacturer_phone_number": "+1 416 555 0123",
"fertiliser_name": "SuperGrow 20-20-20",
"registration_number": "1234567F",
"lot_number": "L987654321",
"weight": [
{
"value":"25",
"unit":"kg"
"value": "25",
"unit": "kg"
},
{
"value":"55",
"unit":"lb"
"value": "55",
"unit": "lb"
}
],
"density":{
"value":"1.2",
"unit":"g/cm"
"density": {
"value": "1.2",
"unit": "g/cm"
},
"volume":{
"value":"20.8",
"unit":"L"
"volume": {
"value": "20.8",
"unit": "L"
},
"npk":"20-20-20",
"cautions_en":[
"npk": "20-20-20",
"cautions_en": [
"Keep out of reach of children.",
"Avoid contact with skin and eyes."
],
"instructions_en":[
"instructions_en": [
"1. Dissolve 50g in 10L of water.",
"2. Apply every 2 weeks.",
"3. Store in a cool, dry place."
],
"cautions_fr":[
"cautions_fr": [
"Tenir hors de portée des enfants.",
"Éviter le contact avec la peau et les yeux."
],
"instructions_fr":[
"instructions_fr": [
"1. Dissoudre 50g dans 10L d'eau.",
"2. Appliquer toutes les 2 semaines.",
"3. Conserver dans un endroit frais et sec."
Expand Down
10 changes: 4 additions & 6 deletions tests/fertiscan/db/queries/test_delete_inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
specification,
sub_label,
)
from fertiscan.db.queries.fertilizer import query_fertilizers

load_dotenv()

Expand Down Expand Up @@ -101,13 +102,10 @@ def test_delete_inspection_success(self):
# TODO: samples not yet handled

# Verify that related fertilizer information was deleted
# TODO: create fertilizer functions
self.cursor.execute(
"SELECT COUNT(*) FROM fertilizer WHERE latest_inspection_id = %s;",
(self.inspection_id,),
fertilizers = query_fertilizers(
cursor=self.cursor, latest_inspection_id=self.inspection_id
)
sample_count = self.cursor.fetchone()[0]
self.assertEqual(sample_count, 0, "Sample should be deleted.")
self.assertListEqual(fertilizers, [])

# Verify that the label information was deleted
self.assertIsNone(
Expand Down
6 changes: 3 additions & 3 deletions tests/fertiscan/db/queries/test_delete_organization_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def tearDown(self):

def test_delete_organization_information_success(self):
# Delete the organization information
# TODO: write delete orga function
# TODO: write missing organization_information functions
self.cursor.execute(
"""
DELETE FROM organization_information
Expand All @@ -72,7 +72,7 @@ def test_delete_organization_information_with_linked_records(self):

# Attempt to delete the organization information and expect a foreign key violation
with self.assertRaises(psycopg.errors.ForeignKeyViolation) as context:
# TODO: write delete orga function
# TODO: write missing organization_information functions
self.cursor.execute(
"""
DELETE FROM organization_information
Expand All @@ -94,7 +94,7 @@ def test_delete_organization_information_with_shared_location(self):
)

# Delete the first organization information
# TODO: write delete orga function
# TODO: write missing organization_information functions
self.cursor.execute(
"""
DELETE FROM organization_information
Expand Down
Loading

0 comments on commit eb777a2

Please sign in to comment.