autocommit 09-07-2024-22-00
This commit is contained in:
parent
52808939b7
commit
83c55333c9
|
|
@ -0,0 +1 @@
|
||||||
|
.vscode
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Make poetry stop complaining
|
||||||
|
|
||||||
|
This is here to prevent poetry from complaining
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
import logging
|
||||||
|
from fastapi import FastAPI, HTTPException
|
||||||
|
from pymongo import MongoClient
|
||||||
|
from bson import ObjectId
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(level=logging.DEBUG,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
logger.debug("FastAPI application initialized")
|
||||||
|
|
||||||
|
# MongoDB connection
|
||||||
|
client = MongoClient("mongodb://localhost:27017")
|
||||||
|
db = client["pos_system"]
|
||||||
|
logger.debug("MongoDB connection established")
|
||||||
|
|
||||||
|
# Models
|
||||||
|
|
||||||
|
|
||||||
|
class Item(BaseModel):
|
||||||
|
name: str
|
||||||
|
price: float
|
||||||
|
quantity: int
|
||||||
|
unit: str
|
||||||
|
related_items: List[str] = []
|
||||||
|
|
||||||
|
|
||||||
|
class Deal(BaseModel):
|
||||||
|
name: str
|
||||||
|
items: List[str]
|
||||||
|
discount: float
|
||||||
|
|
||||||
|
|
||||||
|
class Order(BaseModel):
|
||||||
|
customer_name: str
|
||||||
|
items: List[str]
|
||||||
|
total_amount: float
|
||||||
|
payment_method: str
|
||||||
|
date: str
|
||||||
|
voucher: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
logger.debug("Data models defined")
|
||||||
|
|
||||||
|
# API Routes
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/items/")
|
||||||
|
async def create_item(item: Item):
|
||||||
|
logger.debug(f"Received request to create item: {item}")
|
||||||
|
result = db.items.insert_one(item.dict())
|
||||||
|
logger.debug(f"Item created with ID: {result.inserted_id}")
|
||||||
|
return {"id": str(result.inserted_id)}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/items/")
|
||||||
|
async def read_items():
|
||||||
|
logger.debug("Received request to read all items")
|
||||||
|
items = list(db.items.find())
|
||||||
|
logger.debug(f"Retrieved {len(items)} items")
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/items/{item_id}")
|
||||||
|
async def read_item(item_id: str):
|
||||||
|
logger.debug(f"Received request to read item with ID: {item_id}")
|
||||||
|
item = db.items.find_one({"_id": ObjectId(item_id)})
|
||||||
|
if item:
|
||||||
|
logger.debug(f"Item found: {item}")
|
||||||
|
return item
|
||||||
|
logger.warning(f"Item with ID {item_id} not found")
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/items/{item_id}")
|
||||||
|
async def update_item(item_id: str, item: Item):
|
||||||
|
logger.debug(f"Received request to update item with ID: {item_id}")
|
||||||
|
result = db.items.update_one(
|
||||||
|
{"_id": ObjectId(item_id)}, {"$set": item.dict()})
|
||||||
|
if result.modified_count:
|
||||||
|
logger.debug(f"Item with ID {item_id} updated successfully")
|
||||||
|
return {"message": "Item updated successfully"}
|
||||||
|
logger.warning(f"Item with ID {item_id} not found for update")
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/items/{item_id}")
|
||||||
|
async def delete_item(item_id: str):
|
||||||
|
logger.debug(f"Received request to delete item with ID: {item_id}")
|
||||||
|
result = db.items.delete_one({"_id": ObjectId(item_id)})
|
||||||
|
if result.deleted_count:
|
||||||
|
logger.debug(f"Item with ID {item_id} deleted successfully")
|
||||||
|
return {"message": "Item deleted successfully"}
|
||||||
|
logger.warning(f"Item with ID {item_id} not found for deletion")
|
||||||
|
raise HTTPException(status_code=404, detail="Item not found")
|
||||||
|
|
||||||
|
# Similar CRUD operations for deals and orders
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/inventory/")
|
||||||
|
async def get_inventory():
|
||||||
|
logger.debug("Received request to get inventory")
|
||||||
|
inventory = list(db.items.find({}, {"name": 1, "quantity": 1}))
|
||||||
|
logger.debug(f"Retrieved inventory with {len(inventory)} items")
|
||||||
|
return inventory
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/sales/")
|
||||||
|
async def get_sales_data():
|
||||||
|
# Implement aggregation for sales data
|
||||||
|
# This could include total sales, popular items, etc.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logger.info("Starting the FastAPI application")
|
||||||
|
import uvicorn
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
|
|
||||||
|
# Add more routes as needed for reporting, analytics, etc.
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,46 @@
|
||||||
|
from kivy.app import App
|
||||||
|
from kivy.uix.boxlayout import BoxLayout
|
||||||
|
from kivy.uix.button import Button
|
||||||
|
from kivy.uix.label import Label
|
||||||
|
from kivy.uix.scrollview import ScrollView
|
||||||
|
from kivy.uix.gridlayout import GridLayout
|
||||||
|
|
||||||
|
|
||||||
|
class POSApp(App):
|
||||||
|
def build(self):
|
||||||
|
# Main layout
|
||||||
|
main_layout = BoxLayout(orientation='horizontal')
|
||||||
|
|
||||||
|
# Left navigation bar
|
||||||
|
left_nav = BoxLayout(orientation='vertical', size_hint=(0.2, 1))
|
||||||
|
left_nav.add_widget(Button(text='Inventory'))
|
||||||
|
left_nav.add_widget(Button(text='Orders'))
|
||||||
|
left_nav.add_widget(Button(text='Reports'))
|
||||||
|
main_layout.add_widget(left_nav)
|
||||||
|
|
||||||
|
# Central content area
|
||||||
|
content_area = ScrollView(size_hint=(0.6, 1))
|
||||||
|
grid = GridLayout(cols=3, spacing=10, size_hint_y=None)
|
||||||
|
grid.bind(minimum_height=grid.setter('height'))
|
||||||
|
|
||||||
|
# Add some sample content (replace with actual data from API)
|
||||||
|
for i in range(20):
|
||||||
|
card = BoxLayout(orientation='vertical', size_hint_y=None, height=200)
|
||||||
|
card.add_widget(Label(text=f'Item {i}'))
|
||||||
|
card.add_widget(Label(text=f'Price: ${i*10}'))
|
||||||
|
card.add_widget(Label(text=f'Stock: {i*5}'))
|
||||||
|
grid.add_widget(card)
|
||||||
|
|
||||||
|
content_area.add_widget(grid)
|
||||||
|
main_layout.add_widget(content_area)
|
||||||
|
|
||||||
|
# Right preview panel (initially hidden)
|
||||||
|
right_panel = BoxLayout(orientation='vertical', size_hint=(0.2, 1))
|
||||||
|
right_panel.add_widget(Label(text='Preview Panel'))
|
||||||
|
main_layout.add_widget(right_panel)
|
||||||
|
|
||||||
|
return main_layout
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
POSApp().run()
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
[tool.poetry]
|
||||||
|
name = "py-kivy"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = ""
|
||||||
|
authors = ["Jasen Qin <capitalswine@gmail.com>"]
|
||||||
|
readme = "README.md"
|
||||||
|
package-mode = false
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.12"
|
||||||
|
Kivy = "^2.3.0"
|
||||||
|
fastapi = "^0.111.0"
|
||||||
|
hypothesis = "^6.105.1"
|
||||||
|
pymongo = "^4.8.0"
|
||||||
|
pytest = "^8.2.2"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.autopep8]
|
||||||
|
indent-size = 2
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
# setup_test_db.py
|
||||||
|
|
||||||
|
from pymongo import MongoClient
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_test_db():
|
||||||
|
client = MongoClient("mongodb://localhost:27017")
|
||||||
|
db = client["test_pos_system"]
|
||||||
|
|
||||||
|
# Clear existing data
|
||||||
|
db.items.drop()
|
||||||
|
db.orders.drop()
|
||||||
|
|
||||||
|
# Insert test items
|
||||||
|
items = [
|
||||||
|
{"name": "Apple", "price": 0.5, "quantity": 100, "unit": "piece"},
|
||||||
|
{"name": "Banana", "price": 0.3, "quantity": 150, "unit": "piece"},
|
||||||
|
{"name": "Milk", "price": 2.5, "quantity": 50, "unit": "liter"},
|
||||||
|
{"name": "Bread", "price": 1.5, "quantity": 30, "unit": "loaf"}
|
||||||
|
]
|
||||||
|
result = db.items.insert_many(items)
|
||||||
|
logger.info(f"Inserted {len(result.inserted_ids)} items")
|
||||||
|
|
||||||
|
# Insert test orders
|
||||||
|
orders = [
|
||||||
|
{"customer_name": "John Doe", "items": [
|
||||||
|
"Apple", "Milk"], "total_amount": 3.0, "payment_method": "cash", "date": "2024-03-15"},
|
||||||
|
{"customer_name": "Jane Smith", "items": [
|
||||||
|
"Banana", "Bread"], "total_amount": 1.8, "payment_method": "credit_card", "date": "2024-03-16"}
|
||||||
|
]
|
||||||
|
result = db.orders.insert_many(orders)
|
||||||
|
logger.info(f"Inserted {len(result.inserted_ids)} orders")
|
||||||
|
|
||||||
|
logger.info("Test database setup complete")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
setup_test_db()
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
import pytest
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
from hypothesis import given, strategies as st
|
||||||
|
from bson import ObjectId
|
||||||
|
from fastapi_server import app, db # Import your FastAPI app and database connection
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
# Helper function to clear the database before each test
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def clear_db():
|
||||||
|
db.items.delete_many({})
|
||||||
|
yield
|
||||||
|
db.items.delete_many({})
|
||||||
|
|
||||||
|
# Traditional unit tests
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_item():
|
||||||
|
response = client.post(
|
||||||
|
"/items/",
|
||||||
|
json={"name": "Test Item", "price": 10.99, "quantity": 5, "unit": "piece"}
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "id" in response.json()
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_items():
|
||||||
|
# Create a test item
|
||||||
|
client.post("/items/", json={"name": "Test Item",
|
||||||
|
"price": 10.99, "quantity": 5, "unit": "piece"})
|
||||||
|
|
||||||
|
response = client.get("/items/")
|
||||||
|
assert response.status_code == 200
|
||||||
|
items = response.json()
|
||||||
|
assert len(items) > 0
|
||||||
|
assert items[0]["name"] == "Test Item"
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_item():
|
||||||
|
# Create a test item
|
||||||
|
create_response = client.post(
|
||||||
|
"/items/", json={"name": "Test Item", "price": 10.99, "quantity": 5, "unit": "piece"})
|
||||||
|
item_id = create_response.json()["id"]
|
||||||
|
|
||||||
|
response = client.get(f"/items/{item_id}")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["name"] == "Test Item"
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_item():
|
||||||
|
# Create a test item
|
||||||
|
create_response = client.post(
|
||||||
|
"/items/", json={"name": "Test Item", "price": 10.99, "quantity": 5, "unit": "piece"})
|
||||||
|
item_id = create_response.json()["id"]
|
||||||
|
|
||||||
|
update_data = {"name": "Updated Item",
|
||||||
|
"price": 15.99, "quantity": 10, "unit": "piece"}
|
||||||
|
response = client.put(f"/items/{item_id}", json=update_data)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["message"] == "Item updated successfully"
|
||||||
|
|
||||||
|
# Verify the update
|
||||||
|
get_response = client.get(f"/items/{item_id}")
|
||||||
|
assert get_response.json()["name"] == "Updated Item"
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_item():
|
||||||
|
# Create a test item
|
||||||
|
create_response = client.post(
|
||||||
|
"/items/", json={"name": "Test Item", "price": 10.99, "quantity": 5, "unit": "piece"})
|
||||||
|
item_id = create_response.json()["id"]
|
||||||
|
|
||||||
|
response = client.delete(f"/items/{item_id}")
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["message"] == "Item deleted successfully"
|
||||||
|
|
||||||
|
# Verify the deletion
|
||||||
|
get_response = client.get(f"/items/{item_id}")
|
||||||
|
assert get_response.status_code == 404
|
||||||
|
|
||||||
|
# Property-based tests
|
||||||
|
|
||||||
|
|
||||||
|
@given(
|
||||||
|
name=st.text(min_size=1, max_size=50),
|
||||||
|
price=st.floats(min_value=0.01, max_value=1000000,
|
||||||
|
allow_nan=False, allow_infinity=False),
|
||||||
|
quantity=st.integers(min_value=0, max_value=1000000),
|
||||||
|
unit=st.text(min_size=1, max_size=10)
|
||||||
|
)
|
||||||
|
def test_create_item_property(name, price, quantity, unit):
|
||||||
|
response = client.post(
|
||||||
|
"/items/",
|
||||||
|
json={"name": name, "price": price, "quantity": quantity, "unit": unit}
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "id" in response.json()
|
||||||
|
|
||||||
|
# Verify the created item
|
||||||
|
item_id = response.json()["id"]
|
||||||
|
get_response = client.get(f"/items/{item_id}")
|
||||||
|
assert get_response.status_code == 200
|
||||||
|
item = get_response.json()
|
||||||
|
assert item["name"] == name
|
||||||
|
assert pytest.approx(item["price"], 0.01) == price
|
||||||
|
assert item["quantity"] == quantity
|
||||||
|
assert item["unit"] == unit
|
||||||
|
|
||||||
|
|
||||||
|
@given(
|
||||||
|
st.lists(
|
||||||
|
st.fixed_dictionaries({
|
||||||
|
"name": st.text(min_size=1, max_size=50),
|
||||||
|
"price": st.floats(min_value=0.01, max_value=1000000, allow_nan=False, allow_infinity=False),
|
||||||
|
"quantity": st.integers(min_value=0, max_value=1000000),
|
||||||
|
"unit": st.text(min_size=1, max_size=10)
|
||||||
|
}),
|
||||||
|
min_size=1,
|
||||||
|
max_size=10
|
||||||
|
)
|
||||||
|
)
|
||||||
|
def test_read_items_property(items):
|
||||||
|
# Create multiple items
|
||||||
|
for item in items:
|
||||||
|
client.post("/items/", json=item)
|
||||||
|
|
||||||
|
response = client.get("/items/")
|
||||||
|
assert response.status_code == 200
|
||||||
|
retrieved_items = response.json()
|
||||||
|
assert len(retrieved_items) == len(items)
|
||||||
|
|
||||||
|
for retrieved_item in retrieved_items:
|
||||||
|
assert "name" in retrieved_item
|
||||||
|
assert "price" in retrieved_item
|
||||||
|
assert "quantity" in retrieved_item
|
||||||
|
assert "unit" in retrieved_item
|
||||||
|
|
||||||
|
# Add more property-based tests for update and delete operations if needed
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pytest.main([__file__])
|
||||||
Loading…
Reference in New Issue