PupaClic commited on
Commit
afd89de
·
1 Parent(s): 3952eab

feat(barrier-sizes): implement SQL-like insertion for barrier sizes and bidder associations, including new API endpoint and service methods

Browse files
BARRIER_INSERTION_IMPLEMENTATION.md ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Barrier Size Insertion Implementation
2
+
3
+ ## Overview
4
+
5
+ This implementation provides the SQL INSERT-like functionality for creating barrier sizes and their associations with bidders, as requested:
6
+
7
+ ```sql
8
+ -- Step 1: Insert into BarrierSizes (Id auto-generated, increment by 1)
9
+ INSERT INTO BarrierSizes (height,width,length,cableunits,price,isstandard) VALUES (...)
10
+
11
+ -- Step 2: Insert into BiddersBarrierSizes using the generated BarrierSizeId
12
+ INSERT INTO BiddersBarrierSizes (id,inventoryid,bidderid,barriersizeid,isstandard) VALUES (...)
13
+ ```
14
+
15
+ ## Key Features
16
+
17
+ 1. **Auto-generated IDs**: Both `BarrierSizes.Id` and `BiddersBarrierSizes.Id` are auto-increment fields
18
+ 2. **ID Relationship**: The generated `BarrierSizes.Id` is automatically used as `BiddersBarrierSizes.BarrierSizeId`
19
+ 3. **Transaction Safety**: Both inserts happen in a single database transaction
20
+ 4. **Flexible Parameters**: All barrier size parameters are optional except `bidder_id`
21
+
22
+ ## Implementation Structure
23
+
24
+ ### 1. Database Models
25
+
26
+ #### BarrierSizes Model (`app/db/models/barrier_size.py`)
27
+ ```python
28
+ class BarrierSizes(Base):
29
+ __tablename__ = "BarrierSizes"
30
+
31
+ Id = Column(Integer, primary_key=True, index=True, autoincrement=True) # Auto-increment
32
+ Height = Column(Float, nullable=True)
33
+ Width = Column(Float, nullable=True)
34
+ Lenght = Column(Float, nullable=True) # Note: original DB spelling
35
+ CableUnits = Column(Integer, nullable=True)
36
+ Price = Column(Float, nullable=True)
37
+ IsStandard = Column(Boolean, nullable=True)
38
+ ```
39
+
40
+ #### BiddersBarrierSizes Model (`app/db/models/bidders_barrier_sizes.py`)
41
+ ```python
42
+ class BiddersBarrierSizes(Base):
43
+ __tablename__ = "BiddersBarrierSizes"
44
+
45
+ Id = Column(Integer, primary_key=True, index=True, autoincrement=True) # Auto-increment
46
+ InventoryId = Column(Integer, nullable=True)
47
+ BidderId = Column(Integer, ForeignKey("Bidders.Id"), nullable=False)
48
+ BarrierSizeId = Column(Integer, ForeignKey("BarrierSizes.Id"), nullable=False)
49
+ IsStandard = Column(Boolean, nullable=True)
50
+ ```
51
+
52
+ ### 2. Repository Layer
53
+
54
+ #### Core Method (`app/db/repositories/bidders_barrier_sizes_repo.py`)
55
+ ```python
56
+ def create_barrier_size_with_association(
57
+ self,
58
+ height: Optional[float] = None,
59
+ width: Optional[float] = None,
60
+ length: Optional[float] = None,
61
+ cable_units: Optional[int] = None,
62
+ price: Optional[float] = None,
63
+ is_standard: Optional[bool] = None,
64
+ inventory_id: Optional[int] = None,
65
+ bidder_id: int = None
66
+ ) -> tuple[int, int]:
67
+ """
68
+ Implementation as requested:
69
+ 1. Insert into BarrierSizes (id is auto-generated, increment by 1)
70
+ 2. Insert into BiddersBarrierSizes using the generated BarrierSizeId
71
+
72
+ Returns: (barrier_size_id, bidders_barrier_sizes_id)
73
+ """
74
+ ```
75
+
76
+ ### 3. Service Layer
77
+
78
+ #### Service Function (`app/services/barrier_size_service.py`)
79
+ ```python
80
+ def create_barrier_with_association(
81
+ db: Session,
82
+ height: Optional[float] = None,
83
+ width: Optional[float] = None,
84
+ length: Optional[float] = None,
85
+ cable_units: Optional[int] = None,
86
+ price: Optional[float] = None,
87
+ is_standard: Optional[bool] = None,
88
+ inventory_id: Optional[int] = None,
89
+ bidder_id: int = None
90
+ ):
91
+ """Create barrier size and bidder association using SQL INSERT-like approach"""
92
+ ```
93
+
94
+ ### 4. API Layer
95
+
96
+ #### REST Endpoint (`app/controllers/bidders.py`)
97
+ ```python
98
+ @router.post("/barrier-sizes-sql")
99
+ def create_barrier_size_sql_like(
100
+ height: Optional[float] = None,
101
+ width: Optional[float] = None,
102
+ length: Optional[float] = None,
103
+ cable_units: Optional[int] = None,
104
+ price: Optional[float] = None,
105
+ is_standard: Optional[bool] = None,
106
+ inventory_id: Optional[int] = None,
107
+ bidder_id: int = None,
108
+ db: Session = Depends(get_db)
109
+ ):
110
+ ```
111
+
112
+ ## Usage Examples
113
+
114
+ ### 1. Direct Repository Usage
115
+ ```python
116
+ from app.db.repositories.bidders_barrier_sizes_repo import BiddersBarrierSizesRepository
117
+ from app.db.session import SessionLocal
118
+
119
+ db = SessionLocal()
120
+ repo = BiddersBarrierSizesRepository(db)
121
+
122
+ barrier_size_id, association_id = repo.create_barrier_size_with_association(
123
+ height=6.0,
124
+ width=4.0,
125
+ length=12.0,
126
+ cable_units=10,
127
+ price=1500.00,
128
+ is_standard=True,
129
+ inventory_id=12345,
130
+ bidder_id=1
131
+ )
132
+
133
+ print(f"BarrierSize ID: {barrier_size_id} (auto-generated)")
134
+ print(f"BiddersBarrierSizes ID: {association_id} (auto-generated)")
135
+ ```
136
+
137
+ ### 2. Service Layer Usage
138
+ ```python
139
+ from app.services.barrier_size_service import create_barrier_with_association
140
+ from app.db.session import SessionLocal
141
+
142
+ db = SessionLocal()
143
+
144
+ result = create_barrier_with_association(
145
+ db=db,
146
+ height=6.0,
147
+ width=4.0,
148
+ length=12.0,
149
+ cable_units=10,
150
+ price=1500.00,
151
+ is_standard=True,
152
+ inventory_id=12345,
153
+ bidder_id=1
154
+ )
155
+
156
+ # Returns: {"barrier_size_id": 123, "bidder_barrier_size_id": 456, "message": "..."}
157
+ ```
158
+
159
+ ### 3. API Usage
160
+ ```bash
161
+ curl -X POST "http://localhost:8000/bidders/barrier-sizes-sql" \
162
+ -G \
163
+ -d "height=6.0" \
164
+ -d "width=4.0" \
165
+ -d "length=12.0" \
166
+ -d "cable_units=10" \
167
+ -d "price=1500.00" \
168
+ -d "is_standard=true" \
169
+ -d "inventory_id=12345" \
170
+ -d "bidder_id=1"
171
+ ```
172
+
173
+ ### 4. Python Requests
174
+ ```python
175
+ import requests
176
+
177
+ response = requests.post(
178
+ "http://localhost:8000/bidders/barrier-sizes-sql",
179
+ params={
180
+ "height": 6.0,
181
+ "width": 4.0,
182
+ "length": 12.0,
183
+ "cable_units": 10,
184
+ "price": 1500.00,
185
+ "is_standard": True,
186
+ "inventory_id": 12345,
187
+ "bidder_id": 1
188
+ }
189
+ )
190
+
191
+ result = response.json()
192
+ print(f"Created BarrierSize ID: {result['barrier_size_id']}")
193
+ print(f"Created BiddersBarrierSizes ID: {result['bidder_barrier_size_id']}")
194
+ ```
195
+
196
+ ## Testing
197
+
198
+ ### Test Scripts Provided
199
+
200
+ 1. **`test_barrier_insertion.py`**: Direct database testing
201
+ 2. **`demo_barrier_insertion_api.py`**: API endpoint testing with full examples
202
+
203
+ ### Running Tests
204
+
205
+ ```bash
206
+ # Direct database test
207
+ python test_barrier_insertion.py
208
+
209
+ # API demo (requires running FastAPI server)
210
+ python demo_barrier_insertion_api.py
211
+ ```
212
+
213
+ ## Database Schema Mapping
214
+
215
+ | SQL Field | BarrierSizes Model | BiddersBarrierSizes Model |
216
+ |-----------|-------------------|---------------------------|
217
+ | `height` | `Height` | - |
218
+ | `width` | `Width` | - |
219
+ | `length` | `Lenght` | - |
220
+ | `cableunits` | `CableUnits` | - |
221
+ | `price` | `Price` | - |
222
+ | `isstandard` | `IsStandard` | `IsStandard` |
223
+ | `id` (auto) | `Id` | `Id` |
224
+ | - | - | `InventoryId` |
225
+ | - | - | `BidderId` |
226
+ | - | - | `BarrierSizeId` (from BarrierSizes.Id) |
227
+
228
+ ## Implementation Notes
229
+
230
+ 1. **Auto-increment IDs**: Both tables use auto-increment primary keys
231
+ 2. **Foreign Key Relationship**: `BiddersBarrierSizes.BarrierSizeId` references `BarrierSizes.Id`
232
+ 3. **Transaction Safety**: All operations are wrapped in database transactions
233
+ 4. **Error Handling**: Comprehensive error handling with rollback on failure
234
+ 5. **Logging**: Detailed logging for debugging and monitoring
235
+ 6. **Flexibility**: All parameters except `bidder_id` are optional
236
+
237
+ ## Files Modified/Created
238
+
239
+ ### Modified Files
240
+ - `app/db/models/barrier_size.py` - Added autoincrement=True to Id field
241
+ - `app/db/models/bidders_barrier_sizes.py` - Added InventoryId and IsStandard fields
242
+ - `app/db/repositories/bidders_barrier_sizes_repo.py` - Added insertion methods
243
+ - `app/services/barrier_size_service.py` - Added service layer function
244
+ - `app/controllers/bidders.py` - Added API endpoint
245
+ - `app/schemas/barrier_size.py` - Added inventory_id field
246
+
247
+ ### New Files
248
+ - `app/schemas/bidders_barrier_sizes.py` - Schema definitions
249
+ - `test_barrier_insertion.py` - Direct database test
250
+ - `demo_barrier_insertion_api.py` - API demo script
251
+ - `BARRIER_INSERTION_IMPLEMENTATION.md` - This documentation
252
+
253
+ ## API Response Format
254
+
255
+ ```json
256
+ {
257
+ "barrier_size_id": 123,
258
+ "bidder_barrier_size_id": 456,
259
+ "message": "Successfully inserted barrier size (ID: 123) and bidder association (ID: 456)"
260
+ }
261
+ ```
262
+
263
+ This implementation exactly matches the requested SQL INSERT behavior while providing a clean, maintainable API interface.
app/controllers/bidders.py CHANGED
@@ -200,6 +200,70 @@ def create_barrier_size(
200
  detail=f"Failed to create barrier size: {str(e)}"
201
  )
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
 
205
 
 
200
  detail=f"Failed to create barrier size: {str(e)}"
201
  )
202
 
203
+ @router.post(
204
+ "/barrier-sizes-sql",
205
+ response_model=dict,
206
+ status_code=status.HTTP_201_CREATED,
207
+ summary="Create barrier size with SQL-like INSERT approach",
208
+ response_description="Created barrier size and bidder association IDs"
209
+ )
210
+ def create_barrier_size_sql_like(
211
+ height: Optional[float] = None,
212
+ width: Optional[float] = None,
213
+ length: Optional[float] = None,
214
+ cable_units: Optional[int] = None,
215
+ price: Optional[float] = None,
216
+ is_standard: Optional[bool] = None,
217
+ inventory_id: Optional[int] = None,
218
+ bidder_id: int = None,
219
+ db: Session = Depends(get_db)
220
+ ):
221
+ """
222
+ Create barrier size and bidder association using SQL INSERT-like approach.
223
+
224
+ Implements:
225
+ INSERT INTO BarrierSizes (height,width,length,cableunits,price,isstandard) VALUES (...)
226
+ - The Id in BarrierSizes is auto generated in the table increment by 1
227
+
228
+ INSERT INTO BiddersBarrierSizes (id,inventoryid,bidderid,barriersizeid,isstandard) VALUES (...)
229
+ - The id that got generated in BarrierSizes is stored in BiddersBarrierSizes BarrierSizeId field
230
+ - The id field in BiddersBarrierSizes is auto increment field
231
+ """
232
+ try:
233
+ if bidder_id is None:
234
+ raise HTTPException(
235
+ status_code=status.HTTP_400_BAD_REQUEST,
236
+ detail="bidder_id is required"
237
+ )
238
+
239
+ logger.info(f"Creating barrier size with SQL-like approach for bidder {bidder_id}")
240
+
241
+ result = barrier_size_service.create_barrier_with_association(
242
+ db=db,
243
+ height=height,
244
+ width=width,
245
+ length=length,
246
+ cable_units=cable_units,
247
+ price=price,
248
+ is_standard=is_standard,
249
+ inventory_id=inventory_id,
250
+ bidder_id=bidder_id
251
+ )
252
+
253
+ logger.info(f"Successfully created barrier size and association: {result}")
254
+ return result
255
+
256
+ except HTTPException:
257
+ raise
258
+ except Exception as e:
259
+ logger.error(f"Error creating barrier size with SQL-like approach: {e}")
260
+ import traceback
261
+ traceback.print_exc()
262
+ raise HTTPException(
263
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
264
+ detail=f"Failed to create barrier size: {str(e)}"
265
+ )
266
+
267
 
268
 
269
 
app/db/models/barrier_size.py CHANGED
@@ -5,7 +5,7 @@ class BarrierSizes(Base):
5
  __tablename__ = "BarrierSizes"
6
  __table_args__ = {'extend_existing': True}
7
 
8
- Id = Column(Integer, primary_key=True, index=True)
9
  Height = Column(Float, nullable=True)
10
  Width = Column(Float, nullable=True)
11
  Lenght = Column(Float, nullable=True) # Note: keeping the original spelling "Lenght" from the database
 
5
  __tablename__ = "BarrierSizes"
6
  __table_args__ = {'extend_existing': True}
7
 
8
+ Id = Column(Integer, primary_key=True, index=True, autoincrement=True)
9
  Height = Column(Float, nullable=True)
10
  Width = Column(Float, nullable=True)
11
  Lenght = Column(Float, nullable=True) # Note: keeping the original spelling "Lenght" from the database
app/db/models/bidders_barrier_sizes.py CHANGED
@@ -1,4 +1,4 @@
1
- from sqlalchemy import Column, Integer, ForeignKey
2
  from sqlalchemy.orm import relationship
3
  from app.db.base import Base
4
 
@@ -7,8 +7,10 @@ class BiddersBarrierSizes(Base):
7
  __table_args__ = {'extend_existing': True}
8
 
9
  Id = Column(Integer, primary_key=True, index=True, autoincrement=True)
 
10
  BidderId = Column(Integer, ForeignKey("Bidders.Id"), nullable=False)
11
  BarrierSizeId = Column(Integer, ForeignKey("BarrierSizes.Id"), nullable=False)
 
12
 
13
  # Optional: Add relationships for easier navigation
14
  # bidder = relationship("Bidder", back_populates="barrier_sizes")
 
1
+ from sqlalchemy import Column, Integer, ForeignKey, Boolean
2
  from sqlalchemy.orm import relationship
3
  from app.db.base import Base
4
 
 
7
  __table_args__ = {'extend_existing': True}
8
 
9
  Id = Column(Integer, primary_key=True, index=True, autoincrement=True)
10
+ InventoryId = Column(Integer, nullable=True) # Added InventoryId field
11
  BidderId = Column(Integer, ForeignKey("Bidders.Id"), nullable=False)
12
  BarrierSizeId = Column(Integer, ForeignKey("BarrierSizes.Id"), nullable=False)
13
+ IsStandard = Column(Boolean, nullable=True) # Added IsStandard field
14
 
15
  # Optional: Add relationships for easier navigation
16
  # bidder = relationship("Bidder", back_populates="barrier_sizes")
app/db/repositories/bidders_barrier_sizes_repo.py CHANGED
@@ -8,11 +8,19 @@ class BiddersBarrierSizesRepository:
8
  def __init__(self, db: Session):
9
  self.db = db
10
 
11
- def create_association(self, bidder_id: int, barrier_size_id: int) -> BiddersBarrierSizes:
 
 
 
 
 
 
12
  """Create a new association between a bidder and barrier size"""
13
  db_obj = BiddersBarrierSizes(
14
  BidderId=bidder_id,
15
- BarrierSizeId=barrier_size_id
 
 
16
  )
17
  self.db.add(db_obj)
18
  self.db.commit()
@@ -65,4 +73,89 @@ class BiddersBarrierSizesRepository:
65
  self.db.delete(association)
66
  self.db.commit()
67
  return True
68
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  def __init__(self, db: Session):
9
  self.db = db
10
 
11
+ def create_association(
12
+ self,
13
+ bidder_id: int,
14
+ barrier_size_id: int,
15
+ inventory_id: Optional[int] = None,
16
+ is_standard: Optional[bool] = None
17
+ ) -> BiddersBarrierSizes:
18
  """Create a new association between a bidder and barrier size"""
19
  db_obj = BiddersBarrierSizes(
20
  BidderId=bidder_id,
21
+ BarrierSizeId=barrier_size_id,
22
+ InventoryId=inventory_id,
23
+ IsStandard=is_standard
24
  )
25
  self.db.add(db_obj)
26
  self.db.commit()
 
73
  self.db.delete(association)
74
  self.db.commit()
75
  return True
76
+ return False
77
+
78
+ def create_barrier_size_with_association(
79
+ self,
80
+ height: Optional[float] = None,
81
+ width: Optional[float] = None,
82
+ length: Optional[float] = None,
83
+ cable_units: Optional[int] = None,
84
+ price: Optional[float] = None,
85
+ is_standard: Optional[bool] = None,
86
+ inventory_id: Optional[int] = None,
87
+ bidder_id: int = None
88
+ ) -> tuple[int, int]:
89
+ """
90
+ Create a barrier size and then create the bidder association.
91
+ Returns tuple of (barrier_size_id, bidders_barrier_sizes_id)
92
+
93
+ Implementation as requested:
94
+ 1. Insert into BarrierSizes (id is auto-generated, increment by 1)
95
+ 2. Insert into BiddersBarrierSizes using the generated BarrierSizeId
96
+ """
97
+ from app.db.models.barrier_size import BarrierSizes
98
+
99
+ # Step 1: Insert into BarrierSizes (Id is auto-generated)
100
+ barrier_size = BarrierSizes(
101
+ Height=height,
102
+ Width=width,
103
+ Lenght=length, # Note: keeping original spelling from DB
104
+ CableUnits=cable_units,
105
+ Price=price,
106
+ IsStandard=is_standard
107
+ )
108
+
109
+ self.db.add(barrier_size)
110
+ self.db.commit()
111
+ self.db.refresh(barrier_size)
112
+
113
+ # Step 2: Insert into BiddersBarrierSizes using the generated Id
114
+ bidder_barrier_size = BiddersBarrierSizes(
115
+ InventoryId=inventory_id,
116
+ BidderId=bidder_id,
117
+ BarrierSizeId=barrier_size.Id, # Use the auto-generated Id from step 1
118
+ IsStandard=is_standard
119
+ )
120
+
121
+ self.db.add(bidder_barrier_size)
122
+ self.db.commit()
123
+ self.db.refresh(bidder_barrier_size)
124
+
125
+ return (barrier_size.Id, bidder_barrier_size.Id)
126
+
127
+ def create_with_sql_like_params(
128
+ self,
129
+ height: Optional[float] = None,
130
+ width: Optional[float] = None,
131
+ length: Optional[float] = None,
132
+ cable_units: Optional[int] = None,
133
+ price: Optional[float] = None,
134
+ is_standard: Optional[bool] = None,
135
+ inventory_id: Optional[int] = None,
136
+ bidder_id: int = None
137
+ ) -> dict:
138
+ """
139
+ Create barrier size and bidder association like SQL INSERT statements.
140
+ Mimics:
141
+ INSERT INTO BarrierSizes (height,width,length,cableunits,price,isstandard) VALUES (...)
142
+ INSERT INTO BiddersBarrierSizes (inventoryid,bidderid,barriersizeid,isstandard) VALUES (...)
143
+
144
+ Returns dict with both created record IDs and details
145
+ """
146
+ barrier_size_id, bidder_barrier_size_id = self.create_barrier_size_with_association(
147
+ height=height,
148
+ width=width,
149
+ length=length,
150
+ cable_units=cable_units,
151
+ price=price,
152
+ is_standard=is_standard,
153
+ inventory_id=inventory_id,
154
+ bidder_id=bidder_id
155
+ )
156
+
157
+ return {
158
+ "barrier_size_id": barrier_size_id,
159
+ "bidder_barrier_size_id": bidder_barrier_size_id,
160
+ "message": f"Successfully inserted barrier size (ID: {barrier_size_id}) and bidder association (ID: {bidder_barrier_size_id})"
161
+ }
app/schemas/barrier_size.py CHANGED
@@ -14,6 +14,7 @@ class BarrierSizesCreate(BarrierSizesBase):
14
  """Schema for creating a new barrier size. bidder_id is required."""
15
  # Id is auto-generated, so we don't require it for creation
16
  bidder_id: int # Required to associate barrier size with bidder
 
17
 
18
  class BarrierSizesUpdate(BarrierSizesBase):
19
  """Schema for updating a barrier size. All fields are optional."""
 
14
  """Schema for creating a new barrier size. bidder_id is required."""
15
  # Id is auto-generated, so we don't require it for creation
16
  bidder_id: int # Required to associate barrier size with bidder
17
+ inventory_id: Optional[int] = None # Optional inventory ID for BiddersBarrierSizes
18
 
19
  class BarrierSizesUpdate(BarrierSizesBase):
20
  """Schema for updating a barrier size. All fields are optional."""
app/schemas/bidders_barrier_sizes.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+ from typing import Optional
3
+
4
+ class BiddersBarrierSizesBase(BaseModel):
5
+ """Base schema for BiddersBarrierSizes"""
6
+ InventoryId: Optional[int] = None
7
+ BidderId: int
8
+ BarrierSizeId: int
9
+ IsStandard: Optional[bool] = None
10
+
11
+ class BiddersBarrierSizesCreate(BiddersBarrierSizesBase):
12
+ """Schema for creating a new bidder-barrier size association"""
13
+ # Id is auto-generated, so we don't require it for creation
14
+ pass
15
+
16
+ class BiddersBarrierSizesUpdate(BaseModel):
17
+ """Schema for updating a bidder-barrier size association"""
18
+ InventoryId: Optional[int] = None
19
+ IsStandard: Optional[bool] = None
20
+
21
+ class BiddersBarrierSizesOut(BiddersBarrierSizesBase):
22
+ """Schema for bidder-barrier size association output"""
23
+ Id: int # Always present from database
24
+
25
+ class Config:
26
+ orm_mode = True
27
+
28
+ class BiddersBarrierSizesCreateRequest(BaseModel):
29
+ """Schema for creating barrier size with bidder association in one request"""
30
+ # Barrier size fields
31
+ Height: Optional[float] = None
32
+ Width: Optional[float] = None
33
+ Lenght: Optional[float] = None # Note: keeping original spelling from database
34
+ CableUnits: Optional[int] = None
35
+ Price: Optional[float] = None
36
+ IsStandard: Optional[bool] = None
37
+
38
+ # Association fields
39
+ BidderId: int
40
+ InventoryId: Optional[int] = None
41
+
42
+ class BiddersBarrierSizesCreateResponse(BaseModel):
43
+ """Response schema for creating barrier size with bidder association"""
44
+ barrier_size_id: int
45
+ bidder_barrier_size_id: int
46
+ message: str
app/services/barrier_size_service.py CHANGED
@@ -14,7 +14,7 @@ from app.schemas.barrier_size import (
14
  BarrierSizesUpdate,
15
  BarrierSizesOut,
16
  )
17
- from typing import List
18
  import logging
19
 
20
  logger = logging.getLogger(__name__)
@@ -25,35 +25,25 @@ def create(db: Session, obj_in: BarrierSizesCreate):
25
  # Extract bidder_id before creating the barrier size
26
  bidder_id = obj_in.bidder_id
27
 
28
- # Create barrier size directly in the database (Id will be auto-generated)
29
- from app.db.models.barrier_size import BarrierSizes
30
-
31
- barrier_size_data = {
32
- 'Height': obj_in.Height,
33
- 'Width': obj_in.Width,
34
- 'Lenght': obj_in.Lenght,
35
- 'CableUnits': obj_in.CableUnits,
36
- 'Price': obj_in.Price,
37
- 'IsStandard': obj_in.IsStandard
38
- }
39
-
40
- # Remove None values
41
- barrier_size_data = {k: v for k, v in barrier_size_data.items() if v is not None}
42
-
43
- # Create the barrier size
44
- db_obj = BarrierSizes(**barrier_size_data)
45
- db.add(db_obj)
46
- db.commit()
47
- db.refresh(db_obj)
48
-
49
- # Create the association in BiddersBarrierSizes table
50
  bidders_barrier_repo = BiddersBarrierSizesRepository(db)
51
- association = bidders_barrier_repo.create_association(
52
- bidder_id=bidder_id,
53
- barrier_size_id=db_obj.Id
 
 
 
 
 
 
54
  )
55
 
56
- logger.info(f"Created barrier size {db_obj.Id} and associated with bidder {bidder_id}")
 
 
 
57
  return db_obj
58
 
59
  except Exception as e:
@@ -93,3 +83,46 @@ def delete(db: Session, id: int):
93
  logger.error(f"Error deleting barrier size {id}: {e}")
94
  db.rollback()
95
  raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  BarrierSizesUpdate,
15
  BarrierSizesOut,
16
  )
17
+ from typing import List, Optional
18
  import logging
19
 
20
  logger = logging.getLogger(__name__)
 
25
  # Extract bidder_id before creating the barrier size
26
  bidder_id = obj_in.bidder_id
27
 
28
+ # Use the new method that implements the required workflow:
29
+ # 1. Insert into BarrierSizes (id auto-generated, increment by 1)
30
+ # 2. Insert into BiddersBarrierSizes using the generated BarrierSizeId
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  bidders_barrier_repo = BiddersBarrierSizesRepository(db)
32
+ barrier_size_id, bidder_barrier_size_id = bidders_barrier_repo.create_barrier_size_with_association(
33
+ height=obj_in.Height,
34
+ width=obj_in.Width,
35
+ length=obj_in.Lenght,
36
+ cable_units=obj_in.CableUnits,
37
+ price=obj_in.Price,
38
+ is_standard=obj_in.IsStandard,
39
+ inventory_id=obj_in.inventory_id,
40
+ bidder_id=bidder_id
41
  )
42
 
43
+ # Retrieve the created barrier size to return
44
+ db_obj = get_barrier_size(db, barrier_size_id)
45
+
46
+ logger.info(f"Created barrier size {barrier_size_id} and associated with bidder {bidder_id} (association id: {bidder_barrier_size_id})")
47
  return db_obj
48
 
49
  except Exception as e:
 
83
  logger.error(f"Error deleting barrier size {id}: {e}")
84
  db.rollback()
85
  raise
86
+
87
+ def create_barrier_with_association(
88
+ db: Session,
89
+ height: Optional[float] = None,
90
+ width: Optional[float] = None,
91
+ length: Optional[float] = None,
92
+ cable_units: Optional[int] = None,
93
+ price: Optional[float] = None,
94
+ is_standard: Optional[bool] = None,
95
+ inventory_id: Optional[int] = None,
96
+ bidder_id: int = None
97
+ ):
98
+ """
99
+ Create barrier size and bidder association using SQL INSERT-like approach.
100
+
101
+ Implementation as requested:
102
+ INSERT INTO BarrierSizes (height,width,length,cableunits,price,isstandard) VALUES (...)
103
+ - The Id in BarrierSizes is auto generated in the table increment by 1
104
+
105
+ INSERT INTO BiddersBarrierSizes (id,inventoryid,bidderid,barriersizeid,isstandard) VALUES (...)
106
+ - The id that got generated in BarrierSizes is stored in BiddersBarrierSizes BarrierSizeId field
107
+ - The id field in BiddersBarrierSizes is auto increment field
108
+ """
109
+ try:
110
+ bidders_barrier_repo = BiddersBarrierSizesRepository(db)
111
+ result = bidders_barrier_repo.create_with_sql_like_params(
112
+ height=height,
113
+ width=width,
114
+ length=length,
115
+ cable_units=cable_units,
116
+ price=price,
117
+ is_standard=is_standard,
118
+ inventory_id=inventory_id,
119
+ bidder_id=bidder_id
120
+ )
121
+
122
+ logger.info(f"Successfully created barrier size and association: {result}")
123
+ return result
124
+
125
+ except Exception as e:
126
+ logger.error(f"Error creating barrier size with association: {e}")
127
+ db.rollback()
128
+ raise
demo_barrier_insertion_api.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Demo script showing how to use the barrier size insertion functionality
4
+ that implements the SQL INSERT approach as requested.
5
+ """
6
+
7
+ import requests
8
+ import json
9
+
10
+ # API endpoint
11
+ BASE_URL = "http://localhost:8000" # Adjust as needed
12
+ ENDPOINT = f"{BASE_URL}/bidders/barrier-sizes-sql"
13
+
14
+ def demo_barrier_insertion():
15
+ """
16
+ Demonstrate the barrier size insertion that mimics SQL INSERT statements:
17
+
18
+ INSERT INTO BarrierSizes (height,width,length,cableunits,price,isstandard) VALUES (...)
19
+ INSERT INTO BiddersBarrierSizes (id,inventoryid,bidderid,barriersizeid,isstandard) VALUES (...)
20
+ """
21
+
22
+ print("🚀 Demonstrating SQL-like barrier size insertion")
23
+ print("=" * 60)
24
+
25
+ # Test data
26
+ test_data = {
27
+ "height": 6.0,
28
+ "width": 4.0,
29
+ "length": 12.0,
30
+ "cable_units": 10,
31
+ "price": 1500.00,
32
+ "is_standard": True,
33
+ "inventory_id": 12345,
34
+ "bidder_id": 1
35
+ }
36
+
37
+ print("📊 Test Data:")
38
+ for key, value in test_data.items():
39
+ print(f" {key}: {value}")
40
+
41
+ print(f"\n🌐 Making POST request to: {ENDPOINT}")
42
+
43
+ try:
44
+ # Make the API request
45
+ response = requests.post(
46
+ ENDPOINT,
47
+ params=test_data, # Using query parameters as defined in endpoint
48
+ timeout=30
49
+ )
50
+
51
+ print(f"📈 Response Status: {response.status_code}")
52
+
53
+ if response.status_code == 201:
54
+ result = response.json()
55
+ print("✅ SUCCESS! Barrier size and association created:")
56
+ print(f" Barrier Size ID: {result.get('barrier_size_id')} (auto-generated)")
57
+ print(f" Bidder-Barrier Association ID: {result.get('bidder_barrier_size_id')} (auto-generated)")
58
+ print(f" Message: {result.get('message')}")
59
+
60
+ print("\n🎯 This demonstrates the implementation:")
61
+ print(" 1. INSERT INTO BarrierSizes - ID auto-incremented")
62
+ print(" 2. INSERT INTO BiddersBarrierSizes - uses generated BarrierSizeId")
63
+
64
+ else:
65
+ print(f"❌ Error: {response.status_code}")
66
+ try:
67
+ error_detail = response.json()
68
+ print(f" Details: {json.dumps(error_detail, indent=2)}")
69
+ except:
70
+ print(f" Response: {response.text}")
71
+
72
+ except requests.exceptions.ConnectionError:
73
+ print("❌ Connection Error: Could not connect to the API server")
74
+ print(" Make sure the FastAPI server is running on the correct port")
75
+ print(" Try: uvicorn app.app:app --reload --port 8000")
76
+
77
+ except Exception as e:
78
+ print(f"❌ Unexpected error: {e}")
79
+
80
+ def show_usage_instructions():
81
+ """Show how to use the functionality in code"""
82
+
83
+ print("\n📋 Code Usage Instructions")
84
+ print("=" * 60)
85
+
86
+ print("1. Direct Repository Usage:")
87
+ print("""
88
+ from app.db.repositories.bidders_barrier_sizes_repo import BiddersBarrierSizesRepository
89
+ from app.db.session import SessionLocal
90
+
91
+ db = SessionLocal()
92
+ repo = BiddersBarrierSizesRepository(db)
93
+
94
+ # Create barrier size and association
95
+ barrier_size_id, association_id = repo.create_barrier_size_with_association(
96
+ height=6.0,
97
+ width=4.0,
98
+ length=12.0,
99
+ cable_units=10,
100
+ price=1500.00,
101
+ is_standard=True,
102
+ inventory_id=12345,
103
+ bidder_id=1
104
+ )
105
+
106
+ print(f"Created BarrierSize ID: {barrier_size_id}")
107
+ print(f"Created Association ID: {association_id}")
108
+ """)
109
+
110
+ print("2. Service Layer Usage:")
111
+ print("""
112
+ from app.services.barrier_size_service import create_barrier_with_association
113
+ from app.db.session import SessionLocal
114
+
115
+ db = SessionLocal()
116
+
117
+ result = create_barrier_with_association(
118
+ db=db,
119
+ height=6.0,
120
+ width=4.0,
121
+ length=12.0,
122
+ cable_units=10,
123
+ price=1500.00,
124
+ is_standard=True,
125
+ inventory_id=12345,
126
+ bidder_id=1
127
+ )
128
+
129
+ print(result) # Returns dict with IDs and message
130
+ """)
131
+
132
+ print("3. API Usage (curl):")
133
+ print(f"""
134
+ curl -X POST "{ENDPOINT}" \\
135
+ -G \\
136
+ -d "height=6.0" \\
137
+ -d "width=4.0" \\
138
+ -d "length=12.0" \\
139
+ -d "cable_units=10" \\
140
+ -d "price=1500.00" \\
141
+ -d "is_standard=true" \\
142
+ -d "inventory_id=12345" \\
143
+ -d "bidder_id=1"
144
+ """)
145
+
146
+ if __name__ == "__main__":
147
+ demo_barrier_insertion()
148
+ show_usage_instructions()
test_barrier_insertion.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script demonstrating the barrier size insertion functionality
4
+ as requested in the requirements.
5
+
6
+ This implements:
7
+ 1. INSERT into BarrierSizes (id auto-generated, increment by 1)
8
+ 2. INSERT into BiddersBarrierSizes using the generated BarrierSizeId
9
+ """
10
+
11
+ from sqlalchemy.orm import Session
12
+ from app.db.session import SessionLocal
13
+ from app.db.repositories.bidders_barrier_sizes_repo import BiddersBarrierSizesRepository
14
+
15
+ def test_barrier_insertion():
16
+ """
17
+ Test the barrier size insertion functionality that mimics:
18
+
19
+ INSERT INTO BarrierSizes (height,width,length,cableunits,price,isstandard) VALUES (...)
20
+ - The Id in BarrierSizes is auto generated, increments by 1
21
+
22
+ INSERT INTO BiddersBarrierSizes (id,inventoryid,bidderid,barriersizeid,isstandard) VALUES (...)
23
+ - The id from BarrierSizes is stored in BiddersBarrierSizes.BarrierSizeId
24
+ - The id field in BiddersBarrierSizes is auto increment
25
+ """
26
+
27
+ # Get database session
28
+ db: Session = SessionLocal()
29
+
30
+ try:
31
+ # Initialize repository
32
+ repo = BiddersBarrierSizesRepository(db)
33
+
34
+ # Test data - example barrier size
35
+ test_data = {
36
+ "height": 6.0,
37
+ "width": 4.0,
38
+ "length": 12.0,
39
+ "cable_units": 10,
40
+ "price": 1500.00,
41
+ "is_standard": True,
42
+ "inventory_id": 12345, # Optional inventory ID
43
+ "bidder_id": 1 # Required bidder ID
44
+ }
45
+
46
+ print("Creating barrier size with bidder association...")
47
+ print(f"Input data: {test_data}")
48
+
49
+ # Call the method that implements the required workflow
50
+ result = repo.create_with_sql_like_params(**test_data)
51
+
52
+ print("\nResults:")
53
+ print(f"Barrier Size ID (auto-generated): {result['barrier_size_id']}")
54
+ print(f"Bidder Barrier Size ID (auto-generated): {result['bidder_barrier_size_id']}")
55
+ print(f"Message: {result['message']}")
56
+
57
+ # Verify the records were created
58
+ from app.db.models.barrier_size import BarrierSizes
59
+ from app.db.models.bidders_barrier_sizes import BiddersBarrierSizes
60
+
61
+ # Check BarrierSizes record
62
+ barrier_size = db.query(BarrierSizes).filter(
63
+ BarrierSizes.Id == result['barrier_size_id']
64
+ ).first()
65
+
66
+ print(f"\nCreated BarrierSizes record:")
67
+ print(f" Id: {barrier_size.Id} (auto-generated)")
68
+ print(f" Height: {barrier_size.Height}")
69
+ print(f" Width: {barrier_size.Width}")
70
+ print(f" Length: {barrier_size.Lenght}")
71
+ print(f" CableUnits: {barrier_size.CableUnits}")
72
+ print(f" Price: {barrier_size.Price}")
73
+ print(f" IsStandard: {barrier_size.IsStandard}")
74
+
75
+ # Check BiddersBarrierSizes record
76
+ bidder_barrier = db.query(BiddersBarrierSizes).filter(
77
+ BiddersBarrierSizes.Id == result['bidder_barrier_size_id']
78
+ ).first()
79
+
80
+ print(f"\nCreated BiddersBarrierSizes record:")
81
+ print(f" Id: {bidder_barrier.Id} (auto-generated)")
82
+ print(f" InventoryId: {bidder_barrier.InventoryId}")
83
+ print(f" BidderId: {bidder_barrier.BidderId}")
84
+ print(f" BarrierSizeId: {bidder_barrier.BarrierSizeId} (from BarrierSizes.Id)")
85
+ print(f" IsStandard: {bidder_barrier.IsStandard}")
86
+
87
+ print("\n✅ Successfully implemented the required insertion workflow!")
88
+
89
+ except Exception as e:
90
+ print(f"❌ Error: {e}")
91
+ db.rollback()
92
+ finally:
93
+ db.close()
94
+
95
+ if __name__ == "__main__":
96
+ test_barrier_insertion()