Add endpoint for creating local contacts with access control
Introduced a new `local_contact_router` to handle creation of local contacts. The endpoint enforces role-based access control and ensures no duplication of email addresses. Updated the router exports for consistency and cleaned up a large test file to improve readability.
This commit is contained in:
parent
43d67b1044
commit
f588bc0cda
@ -9,6 +9,8 @@ from .data import (
|
|||||||
dewar_types,
|
dewar_types,
|
||||||
serial_numbers,
|
serial_numbers,
|
||||||
sample_events,
|
sample_events,
|
||||||
|
local_contacts,
|
||||||
|
beamtimes,
|
||||||
)
|
)
|
||||||
from .slots_data import slots
|
from .slots_data import slots
|
||||||
|
|
||||||
@ -24,4 +26,6 @@ __all__ = [
|
|||||||
"serial_numbers",
|
"serial_numbers",
|
||||||
"sample_events",
|
"sample_events",
|
||||||
"slots",
|
"slots",
|
||||||
|
"local_contacts",
|
||||||
|
"beamtimes",
|
||||||
]
|
]
|
||||||
|
@ -10,13 +10,14 @@ from app.models import (
|
|||||||
DewarSerialNumber,
|
DewarSerialNumber,
|
||||||
SampleEvent,
|
SampleEvent,
|
||||||
LogisticsEvent,
|
LogisticsEvent,
|
||||||
|
LocalContact,
|
||||||
|
Beamtime,
|
||||||
)
|
)
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
dewar_types = [
|
dewar_types = [
|
||||||
DewarType(id=1, dewar_type="Type A"),
|
DewarType(id=1, dewar_type="Type A"),
|
||||||
DewarType(id=2, dewar_type="Type B"),
|
DewarType(id=2, dewar_type="Type B"),
|
||||||
@ -373,6 +374,50 @@ specific_dewars1 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids1
|
|||||||
specific_dewars2 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids2]
|
specific_dewars2 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids2]
|
||||||
specific_dewars3 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids3]
|
specific_dewars3 = [dewar for dewar in dewars if dewar.id in specific_dewar_ids3]
|
||||||
|
|
||||||
|
local_contacts = [
|
||||||
|
LocalContact(
|
||||||
|
id=1,
|
||||||
|
firstname="John",
|
||||||
|
lastname="Rambo",
|
||||||
|
phone_number="+410000000",
|
||||||
|
email="john.rambo@war.com",
|
||||||
|
),
|
||||||
|
LocalContact(
|
||||||
|
id=2,
|
||||||
|
firstname="John",
|
||||||
|
lastname="Mclane",
|
||||||
|
phone_number="+9990000099",
|
||||||
|
email="john.mclane@war.com",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
beamtimes = [
|
||||||
|
Beamtime(
|
||||||
|
id=1,
|
||||||
|
pgroups="p20001",
|
||||||
|
beamtime_name="p20001-test",
|
||||||
|
beamline="X06DA",
|
||||||
|
start_date=datetime.strptime("06.02.2025", "%d.%m.%Y").date(),
|
||||||
|
end_date=datetime.strptime("07.02.2025", "%d.%m.%Y").date(),
|
||||||
|
status="confirmed",
|
||||||
|
comments="this is a test beamtime",
|
||||||
|
proposal_id=1,
|
||||||
|
local_contact_id=1,
|
||||||
|
),
|
||||||
|
Beamtime(
|
||||||
|
id=2,
|
||||||
|
pgroups="p20002",
|
||||||
|
beamtime_name="p20001-test",
|
||||||
|
beamline="X06DA",
|
||||||
|
start_date=datetime.strptime("07.02.2025", "%d.%m.%Y").date(),
|
||||||
|
end_date=datetime.strptime("08.02.2025", "%d.%m.%Y").date(),
|
||||||
|
status="confirmed",
|
||||||
|
comments="this is a test beamtime",
|
||||||
|
proposal_id=2,
|
||||||
|
local_contact_id=2,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
# Define shipments
|
# Define shipments
|
||||||
shipments = [
|
shipments = [
|
||||||
Shipment(
|
Shipment(
|
||||||
|
@ -77,6 +77,8 @@ def load_sample_data(session: Session):
|
|||||||
serial_numbers,
|
serial_numbers,
|
||||||
slots,
|
slots,
|
||||||
sample_events,
|
sample_events,
|
||||||
|
local_contacts,
|
||||||
|
beamtimes,
|
||||||
)
|
)
|
||||||
|
|
||||||
# If any data exists, don't reseed
|
# If any data exists, don't reseed
|
||||||
@ -95,5 +97,8 @@ def load_sample_data(session: Session):
|
|||||||
+ serial_numbers
|
+ serial_numbers
|
||||||
+ slots
|
+ slots
|
||||||
+ sample_events
|
+ sample_events
|
||||||
|
+ local_contacts
|
||||||
|
+ beamtimes
|
||||||
)
|
)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
@ -45,6 +45,17 @@ class Contact(Base):
|
|||||||
shipments = relationship("Shipment", back_populates="contact")
|
shipments = relationship("Shipment", back_populates="contact")
|
||||||
|
|
||||||
|
|
||||||
|
class LocalContact(Base):
|
||||||
|
__tablename__ = "local_contacts"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
status = Column(String(255), default="active")
|
||||||
|
firstname = Column(String(255), nullable=False)
|
||||||
|
lastname = Column(String(255), nullable=False)
|
||||||
|
phone_number = Column(String(255), nullable=False)
|
||||||
|
email = Column(String(255), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
class Address(Base):
|
class Address(Base):
|
||||||
__tablename__ = "addresses"
|
__tablename__ = "addresses"
|
||||||
|
|
||||||
@ -103,6 +114,10 @@ class Dewar(Base):
|
|||||||
slot = relationship("Slot", back_populates="dewar")
|
slot = relationship("Slot", back_populates="dewar")
|
||||||
events = relationship("LogisticsEvent", back_populates="dewar")
|
events = relationship("LogisticsEvent", back_populates="dewar")
|
||||||
beamline_location = None
|
beamline_location = None
|
||||||
|
local_contact_id = Column(Integer, ForeignKey("local_contacts.id"), nullable=True)
|
||||||
|
local_contact = relationship("LocalContact")
|
||||||
|
beamtime = relationship("Beamtime", back_populates="dewars")
|
||||||
|
beamtime_id = Column(Integer, ForeignKey("beamtimes.id"), nullable=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def number_of_pucks(self) -> int:
|
def number_of_pucks(self) -> int:
|
||||||
@ -216,6 +231,24 @@ class PuckEvent(Base):
|
|||||||
puck = relationship("Puck", back_populates="events")
|
puck = relationship("Puck", back_populates="events")
|
||||||
|
|
||||||
|
|
||||||
|
class Beamtime(Base):
|
||||||
|
__tablename__ = "beamtimes"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
pgroups = Column(String(255), nullable=False)
|
||||||
|
beamtime_name = Column(String(255), index=True)
|
||||||
|
beamline = Column(String(255), nullable=True)
|
||||||
|
start_date = Column(Date, nullable=True)
|
||||||
|
end_date = Column(Date, nullable=True)
|
||||||
|
status = Column(String(255), nullable=True)
|
||||||
|
comments = Column(String(200), nullable=True)
|
||||||
|
proposal_id = Column(Integer, ForeignKey("proposals.id"), nullable=True)
|
||||||
|
local_contact_id = Column(Integer, ForeignKey("local_contacts.id"), nullable=False)
|
||||||
|
|
||||||
|
local_contact = relationship("LocalContact")
|
||||||
|
dewars = relationship("Dewar", back_populates="beamtime")
|
||||||
|
|
||||||
|
|
||||||
# class Results(Base):
|
# class Results(Base):
|
||||||
# __tablename__ = "results"
|
# __tablename__ = "results"
|
||||||
#
|
#
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from .address import address_router
|
from .address import address_router
|
||||||
from .contact import contact_router
|
from .contact import contact_router
|
||||||
|
from .local_contact import local_contact_router
|
||||||
from .proposal import router as proposal_router
|
from .proposal import router as proposal_router
|
||||||
from .dewar import dewar_router
|
from .dewar import dewar_router
|
||||||
from .shipment import shipment_router
|
from .shipment import shipment_router
|
||||||
@ -9,6 +10,7 @@ from .protected_router import protected_router as protected_router
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
"address_router",
|
"address_router",
|
||||||
"contact_router",
|
"contact_router",
|
||||||
|
"local_contact_router",
|
||||||
"proposal_router",
|
"proposal_router",
|
||||||
"dewar_router",
|
"dewar_router",
|
||||||
"shipment_router",
|
"shipment_router",
|
||||||
|
@ -21,6 +21,12 @@ mock_users_db = {
|
|||||||
"password": "testpass2", # In a real scenario, store the hash of the password
|
"password": "testpass2", # In a real scenario, store the hash of the password
|
||||||
"pgroups": ["p20004", "p20005", "p20006"],
|
"pgroups": ["p20004", "p20005", "p20006"],
|
||||||
},
|
},
|
||||||
|
"admin": {
|
||||||
|
"username": "admin",
|
||||||
|
"password": "adminpass",
|
||||||
|
"pgroups": ["p20007"],
|
||||||
|
# "role": "admin",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -49,6 +55,9 @@ async def get_current_user(token: str = Depends(oauth2_scheme)) -> loginData:
|
|||||||
username: str = payload.get("sub")
|
username: str = payload.get("sub")
|
||||||
print(f"[DEBUG] Username decoded from token: {username}") # Add debug log here
|
print(f"[DEBUG] Username decoded from token: {username}") # Add debug log here
|
||||||
return loginData(username=username, pgroups=payload.get("pgroups"))
|
return loginData(username=username, pgroups=payload.get("pgroups"))
|
||||||
|
# return loginData(username=username, pgroups=payload.get("pgroups"),
|
||||||
|
# role=payload.get("role"))
|
||||||
|
|
||||||
except jwt.ExpiredSignatureError:
|
except jwt.ExpiredSignatureError:
|
||||||
print("[DEBUG] Token expired")
|
print("[DEBUG] Token expired")
|
||||||
raise HTTPException(status_code=401, detail="Token expired")
|
raise HTTPException(status_code=401, detail="Token expired")
|
||||||
@ -57,6 +66,14 @@ async def get_current_user(token: str = Depends(oauth2_scheme)) -> loginData:
|
|||||||
raise HTTPException(status_code=401, detail="Invalid token")
|
raise HTTPException(status_code=401, detail="Invalid token")
|
||||||
|
|
||||||
|
|
||||||
|
# async def get_user_role(token: str = Depends(oauth2_scheme)) -> str:
|
||||||
|
# try:
|
||||||
|
# payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||||
|
# return payload.get("role")
|
||||||
|
# except jwt.ExpiredSignatureError:
|
||||||
|
# raise HTTPException(status_code=401, detail="Token expired")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/token/login", response_model=loginToken)
|
@router.post("/token/login", response_model=loginToken)
|
||||||
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||||
user = mock_users_db.get(form_data.username)
|
user = mock_users_db.get(form_data.username)
|
||||||
@ -70,10 +87,14 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
|||||||
# Create token
|
# Create token
|
||||||
access_token = create_access_token(
|
access_token = create_access_token(
|
||||||
data={"sub": user["username"], "pgroups": user["pgroups"]}
|
data={"sub": user["username"], "pgroups": user["pgroups"]}
|
||||||
|
# data = {"sub": user["username"], "pgroups": user["pgroups"],
|
||||||
|
# "role": user["role"]}
|
||||||
)
|
)
|
||||||
return loginToken(access_token=access_token, token_type="bearer")
|
return loginToken(access_token=access_token, token_type="bearer")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/protected-route")
|
@router.get("/protected-route")
|
||||||
async def read_protected_data(current_user: loginData = Depends(get_current_user)):
|
async def read_protected_data(current_user: loginData = Depends(get_current_user)):
|
||||||
|
# return {"username": current_user.username, "pgroups":
|
||||||
|
# current_user.pgroups, "role": current_user.role}
|
||||||
return {"username": current_user.username, "pgroups": current_user.pgroups}
|
return {"username": current_user.username, "pgroups": current_user.pgroups}
|
||||||
|
56
backend/app/routers/local_contact.py
Normal file
56
backend/app/routers/local_contact.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
from fastapi import APIRouter, HTTPException, status, Depends
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app.models import LocalContact as LocalContactModel
|
||||||
|
from app.schemas import LocalContactCreate as LocalContactSchema, loginData
|
||||||
|
from app.dependencies import get_db
|
||||||
|
from app.routers.auth import get_current_user
|
||||||
|
|
||||||
|
local_contact_router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@local_contact_router.post(
|
||||||
|
"/",
|
||||||
|
response_model=LocalContactSchema,
|
||||||
|
status_code=status.HTTP_201_CREATED,
|
||||||
|
)
|
||||||
|
async def create_local_contact(
|
||||||
|
local_contact: LocalContactSchema,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
current_user: loginData = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Create a new local contact. Only selected users can create a local contact.
|
||||||
|
"""
|
||||||
|
# Access control: Only allow users with specific roles (e.g., "admin" or
|
||||||
|
# "contact_manager")
|
||||||
|
if current_user.role not in ["admin", "contact_manager"]:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="You do not have permission to create a local contact.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if a local contact with the same email already exists
|
||||||
|
if (
|
||||||
|
db.query(LocalContactModel)
|
||||||
|
.filter(LocalContactModel.email == local_contact.email)
|
||||||
|
.first()
|
||||||
|
):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="A local contact with this email already exists.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a new LocalContact
|
||||||
|
db_local_contact = LocalContactModel(
|
||||||
|
firstname=local_contact.firstname,
|
||||||
|
lastname=local_contact.lastname,
|
||||||
|
phone_number=local_contact.phone_number,
|
||||||
|
email=local_contact.email,
|
||||||
|
status=local_contact.status or "active",
|
||||||
|
)
|
||||||
|
db.add(db_local_contact)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_local_contact)
|
||||||
|
|
||||||
|
return db_local_contact
|
@ -351,7 +351,7 @@ async def get_all_dewars(db: Session = Depends(get_db)):
|
|||||||
|
|
||||||
@router.get("/dewar/table", response_model=List[DewarTable])
|
@router.get("/dewar/table", response_model=List[DewarTable])
|
||||||
async def get_all_dewars_table(db: Session = Depends(get_db)):
|
async def get_all_dewars_table(db: Session = Depends(get_db)):
|
||||||
dewars = db.query(DewarModel).all()
|
dewars = db.query(DewarModel).filter(DewarModel.events.any()).all()
|
||||||
|
|
||||||
# Flatten relationships for simplified frontend rendering
|
# Flatten relationships for simplified frontend rendering
|
||||||
response = []
|
response = []
|
||||||
@ -365,6 +365,7 @@ async def get_all_dewars_table(db: Session = Depends(get_db)):
|
|||||||
dewar_name=dewar.dewar_name,
|
dewar_name=dewar.dewar_name,
|
||||||
shipment_name=dewar.shipment.shipment_name if dewar.shipment else "N/A",
|
shipment_name=dewar.shipment.shipment_name if dewar.shipment else "N/A",
|
||||||
# Use the most recent event if available
|
# Use the most recent event if available
|
||||||
|
beamtime=dewar.beamtime,
|
||||||
status=dewar.events[-1].event_type if dewar.events else "No Events",
|
status=dewar.events[-1].event_type if dewar.events else "No Events",
|
||||||
tracking_number=dewar.tracking_number or "N/A",
|
tracking_number=dewar.tracking_number or "N/A",
|
||||||
slot_id=dewar.slot[0].id
|
slot_id=dewar.slot[0].id
|
||||||
|
@ -5,6 +5,7 @@ from app.routers.address import address_router
|
|||||||
from app.routers.contact import contact_router
|
from app.routers.contact import contact_router
|
||||||
from app.routers.shipment import shipment_router
|
from app.routers.shipment import shipment_router
|
||||||
from app.routers.dewar import dewar_router
|
from app.routers.dewar import dewar_router
|
||||||
|
from app.routers.local_contact import local_contact_router
|
||||||
|
|
||||||
protected_router = APIRouter(
|
protected_router = APIRouter(
|
||||||
dependencies=[Depends(get_current_user)] # Applies to all routes
|
dependencies=[Depends(get_current_user)] # Applies to all routes
|
||||||
@ -12,6 +13,9 @@ protected_router = APIRouter(
|
|||||||
|
|
||||||
protected_router.include_router(address_router, prefix="/addresses", tags=["addresses"])
|
protected_router.include_router(address_router, prefix="/addresses", tags=["addresses"])
|
||||||
protected_router.include_router(contact_router, prefix="/contacts", tags=["contacts"])
|
protected_router.include_router(contact_router, prefix="/contacts", tags=["contacts"])
|
||||||
|
protected_router.include_router(
|
||||||
|
local_contact_router, prefix="/local-contacts", tags=["local-contacts"]
|
||||||
|
)
|
||||||
protected_router.include_router(
|
protected_router.include_router(
|
||||||
shipment_router, prefix="/shipments", tags=["shipments"]
|
shipment_router, prefix="/shipments", tags=["shipments"]
|
||||||
)
|
)
|
||||||
|
@ -89,19 +89,19 @@ async def create_sample_event(
|
|||||||
return sample # Return the sample, now including `mount_count`
|
return sample # Return the sample, now including `mount_count`
|
||||||
|
|
||||||
|
|
||||||
@router.post("/samples/{sample_id}/upload-images")
|
@router.post("/{sample_id}/upload-images")
|
||||||
async def upload_sample_images(
|
async def upload_sample_image(
|
||||||
sample_id: int,
|
sample_id: int,
|
||||||
uploaded_files: list[UploadFile] = File(...),
|
uploaded_file: UploadFile = File(...),
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
):
|
):
|
||||||
logging.info(f"Received files: {[file.filename for file in uploaded_files]}")
|
logging.info(f"Received file: {uploaded_file.filename}")
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Uploads images for a given sample and saves them to a directory structure.
|
Uploads an image for a given sample and saves it to a directory structure.
|
||||||
Args:
|
Args:
|
||||||
sample_id (int): ID of the sample.
|
sample_id (int): ID of the sample.
|
||||||
uploaded_files (list[UploadFile]): A list of files uploaded with the request.
|
uploaded_file (UploadFile): The file uploaded with the request.
|
||||||
db (Session): Database session.
|
db (Session): Database session.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -123,35 +123,32 @@ async def upload_sample_images(
|
|||||||
base_dir = Path(f"images/{pgroup}/{today}/{dewar_name}/{puck_name}/{position}")
|
base_dir = Path(f"images/{pgroup}/{today}/{dewar_name}/{puck_name}/{position}")
|
||||||
base_dir.mkdir(parents=True, exist_ok=True)
|
base_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# 3. Process and Save Each File
|
# 3. Validate MIME type and Save the File
|
||||||
saved_files = []
|
if not uploaded_file.content_type.startswith("image/"):
|
||||||
for file in uploaded_files:
|
raise HTTPException(
|
||||||
# Validate MIME type
|
status_code=400,
|
||||||
if not file.content_type.startswith("image/"):
|
detail=f"Invalid file type: {uploaded_file.filename}."
|
||||||
raise HTTPException(
|
f" Only images are accepted.",
|
||||||
status_code=400,
|
)
|
||||||
detail=f"Invalid file type: {file.filename}. Only images are accepted.",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Save file to the base directory
|
file_path = base_dir / uploaded_file.filename
|
||||||
file_path = base_dir / file.filename
|
logging.debug(f"Saving file {uploaded_file.filename} to {file_path}")
|
||||||
|
|
||||||
# Save the file from the file stream
|
try:
|
||||||
try:
|
with file_path.open("wb") as buffer:
|
||||||
with file_path.open("wb") as buffer:
|
shutil.copyfileobj(uploaded_file.file, buffer)
|
||||||
shutil.copyfileobj(file.file, buffer)
|
logging.info(f"File saved: {file_path}")
|
||||||
saved_files.append(str(file_path)) # Track saved file paths
|
except Exception as e:
|
||||||
except Exception as e:
|
logging.error(f"Error saving file {uploaded_file.filename}: {str(e)}")
|
||||||
logging.error(f"Error saving file {file.filename}: {str(e)}")
|
raise HTTPException(
|
||||||
raise HTTPException(
|
status_code=500,
|
||||||
status_code=500,
|
detail=f"Could not save file {uploaded_file.filename}."
|
||||||
detail=f"Could not save file {file.filename}."
|
f" Ensure the server has correct permissions.",
|
||||||
f" Ensure the server has correct permissions.",
|
)
|
||||||
)
|
|
||||||
|
|
||||||
# 4. Return Saved Files Information
|
# 4. Return Saved File Information
|
||||||
logging.info(f"Uploaded {len(saved_files)} files for sample {sample_id}.")
|
logging.info(f"Uploaded 1 file for sample {sample_id}.")
|
||||||
return {
|
return {
|
||||||
"message": f"{len(saved_files)} images uploaded successfully.",
|
"message": "1 image uploaded successfully.",
|
||||||
"files": saved_files,
|
"file": str(file_path),
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ class loginToken(BaseModel):
|
|||||||
class loginData(BaseModel):
|
class loginData(BaseModel):
|
||||||
username: str
|
username: str
|
||||||
pgroups: List[str]
|
pgroups: List[str]
|
||||||
|
# role: Optional[str] = "user"
|
||||||
|
|
||||||
|
|
||||||
class DewarTypeBase(BaseModel):
|
class DewarTypeBase(BaseModel):
|
||||||
@ -417,6 +418,29 @@ class ContactMinimal(BaseModel):
|
|||||||
id: int
|
id: int
|
||||||
|
|
||||||
|
|
||||||
|
class Proposal(BaseModel):
|
||||||
|
id: int
|
||||||
|
number: str
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class LocalContactCreate(BaseModel):
|
||||||
|
firstname: str
|
||||||
|
lastname: str
|
||||||
|
phone_number: str
|
||||||
|
email: EmailStr
|
||||||
|
status: str = "active"
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class LocalContact(LocalContactCreate):
|
||||||
|
id: int
|
||||||
|
|
||||||
|
|
||||||
class AddressCreate(BaseModel):
|
class AddressCreate(BaseModel):
|
||||||
pgroups: str
|
pgroups: str
|
||||||
house_number: Optional[str] = None
|
house_number: Optional[str] = None
|
||||||
@ -617,14 +641,6 @@ class DewarTable(BaseModel):
|
|||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
class Proposal(BaseModel):
|
|
||||||
id: int
|
|
||||||
number: str
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
||||||
|
|
||||||
class Shipment(BaseModel):
|
class Shipment(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
pgroups: str
|
pgroups: str
|
||||||
@ -752,3 +768,18 @@ class PuckWithTellPosition(BaseModel):
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class Beamtime(BaseModel):
|
||||||
|
id: int
|
||||||
|
pgroups: str
|
||||||
|
beamtime_name: str
|
||||||
|
beamline: str
|
||||||
|
start_date: date
|
||||||
|
end_date: date
|
||||||
|
status: str
|
||||||
|
comments: Optional[constr(max_length=200)] = None
|
||||||
|
proposal_id: Optional[int]
|
||||||
|
proposal: Optional[Proposal]
|
||||||
|
local_contact_id: Optional[int]
|
||||||
|
local_contact: Optional[LocalContact]
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "aareDB"
|
name = "aareDB"
|
||||||
version = "0.1.0a21"
|
version = "0.1.0a22"
|
||||||
description = "Backend for next gen sample management system"
|
description = "Backend for next gen sample management system"
|
||||||
authors = [{name = "Guillaume Gotthard", email = "guillaume.gotthard@psi.ch"}]
|
authors = [{name = "Guillaume Gotthard", email = "guillaume.gotthard@psi.ch"}]
|
||||||
license = {text = "MIT"}
|
license = {text = "MIT"}
|
||||||
@ -33,5 +33,5 @@ dependencies = [
|
|||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
norecursedirs = ["backend/python-client"]
|
norecursedirs = ["backend/python-client"]
|
||||||
# Or limit files explicitly
|
# Or limit files explicitly
|
||||||
python_files = ["test_auth.py",
|
python_files = ["test_auth.py"]#,
|
||||||
"test_contact.py"]
|
#"test_contact.py"]
|
@ -1,15 +1,10 @@
|
|||||||
{
|
{
|
||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
|
"metadata": {},
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"id": "initial_id",
|
"outputs": [],
|
||||||
"metadata": {
|
"execution_count": null,
|
||||||
"collapsed": true,
|
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-02-04T12:17:43.144287Z",
|
|
||||||
"start_time": "2025-02-04T12:17:43.141596Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"source": [
|
"source": [
|
||||||
"import json\n",
|
"import json\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -37,26 +32,13 @@
|
|||||||
"configuration.verify_ssl = False # Disable SSL verification\n",
|
"configuration.verify_ssl = False # Disable SSL verification\n",
|
||||||
"#print(dir(SamplesApi))"
|
"#print(dir(SamplesApi))"
|
||||||
],
|
],
|
||||||
"outputs": [
|
"id": "3b7c27697a4d5c83"
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"0.1.0a20\n",
|
|
||||||
"https://127.0.0.1:8000\n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"execution_count": 22
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-02-03T08:48:55.604554Z",
|
|
||||||
"start_time": "2025-02-03T08:48:55.583427Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"## Fetch all Shipments, list corresponding dewars and pucks\n",
|
"## Fetch all Shipments, list corresponding dewars and pucks\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -89,27 +71,13 @@
|
|||||||
" except ApiException as e:\n",
|
" except ApiException as e:\n",
|
||||||
" print(f\"Exception when calling ShipmentsApi->fetch_shipments_shipments_get: {e}\")\n"
|
" print(f\"Exception when calling ShipmentsApi->fetch_shipments_shipments_get: {e}\")\n"
|
||||||
],
|
],
|
||||||
"id": "45cc7ab6d4589711",
|
"id": "4955b858f2cef93e"
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"ename": "AttributeError",
|
|
||||||
"evalue": "'ShipmentsApi' object has no attribute 'fetch_shipments_shipments_get'",
|
|
||||||
"output_type": "error",
|
|
||||||
"traceback": [
|
|
||||||
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
|
|
||||||
"\u001B[0;31mAttributeError\u001B[0m Traceback (most recent call last)",
|
|
||||||
"Cell \u001B[0;32mIn[3], line 12\u001B[0m\n\u001B[1;32m 8\u001B[0m api_instance \u001B[38;5;241m=\u001B[39m aareDBclient\u001B[38;5;241m.\u001B[39mShipmentsApi(api_client)\n\u001B[1;32m 10\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m 11\u001B[0m \u001B[38;5;66;03m# Fetch all shipments\u001B[39;00m\n\u001B[0;32m---> 12\u001B[0m all_shipments_response \u001B[38;5;241m=\u001B[39m \u001B[43mapi_instance\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mfetch_shipments_shipments_get\u001B[49m()\n\u001B[1;32m 14\u001B[0m \u001B[38;5;66;03m# Print shipment names and their associated puck names\u001B[39;00m\n\u001B[1;32m 15\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m shipment \u001B[38;5;129;01min\u001B[39;00m all_shipments_response:\n",
|
|
||||||
"\u001B[0;31mAttributeError\u001B[0m: 'ShipmentsApi' object has no attribute 'fetch_shipments_shipments_get'"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"execution_count": 3
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-02-03T22:26:47.957072Z",
|
"end_time": "2025-02-25T15:47:34.956061Z",
|
||||||
"start_time": "2025-02-03T22:26:47.935362Z"
|
"start_time": "2025-02-25T15:47:34.941372Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
@ -175,44 +143,27 @@
|
|||||||
" except ApiException as e:\n",
|
" except ApiException as e:\n",
|
||||||
" print(f\"Exception when calling LogisticsApi->scan_dewar_logistics_dewar_scan_post: {e}\")\n"
|
" print(f\"Exception when calling LogisticsApi->scan_dewar_logistics_dewar_scan_post: {e}\")\n"
|
||||||
],
|
],
|
||||||
"id": "f5de1787214a6642",
|
"id": "8fd3638bffaecd23",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"ename": "TypeError",
|
||||||
"output_type": "stream",
|
"evalue": "ApiClient.call_api() got an unexpected keyword argument 'path_params'",
|
||||||
"text": [
|
"output_type": "error",
|
||||||
"Exception when calling LogisticsApi->scan_dewar_logistics_dewar_scan_post: (400)\n",
|
"traceback": [
|
||||||
"Reason: Bad Request\n",
|
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
|
||||||
"HTTP response headers: HTTPHeaderDict({'date': 'Mon, 03 Feb 2025 22:26:47 GMT', 'server': 'uvicorn', 'content-length': '47', 'content-type': 'application/json'})\n",
|
"\u001B[0;31mTypeError\u001B[0m Traceback (most recent call last)",
|
||||||
"HTTP response body: {\"detail\":\"Slot not found or already occupied\"}\n",
|
"Cell \u001B[0;32mIn[43], line 19\u001B[0m\n\u001B[1;32m 15\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m 16\u001B[0m \u001B[38;5;28;01mwith\u001B[39;00m \u001B[38;5;28mopen\u001B[39m(file_path, \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mrb\u001B[39m\u001B[38;5;124m\"\u001B[39m) \u001B[38;5;28;01mas\u001B[39;00m file_data:\n\u001B[1;32m 17\u001B[0m \u001B[38;5;66;03m# Use the low-level call_api method; note that the files parameter here is\u001B[39;00m\n\u001B[1;32m 18\u001B[0m \u001B[38;5;66;03m# a dictionary with key matching the FastAPI parameter name.\u001B[39;00m\n\u001B[0;32m---> 19\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[43mapi_client\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mcall_api\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 20\u001B[0m \u001B[43m \u001B[49m\u001B[38;5;124;43mf\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43m/\u001B[39;49m\u001B[38;5;132;43;01m{\u001B[39;49;00m\u001B[43msample_id\u001B[49m\u001B[38;5;132;43;01m}\u001B[39;49;00m\u001B[38;5;124;43m/upload_images\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 21\u001B[0m \u001B[43m \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mPOST\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 22\u001B[0m \u001B[43m \u001B[49m\u001B[43mpath_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m{\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43msample_id\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43msample_id\u001B[49m\u001B[43m}\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 23\u001B[0m \u001B[43m \u001B[49m\u001B[43mfiles\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m{\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43muploaded_file\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m:\u001B[49m\u001B[43m \u001B[49m\u001B[43m(\u001B[49m\u001B[43mfilename\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mfile_data\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mmime_type\u001B[49m\u001B[43m)\u001B[49m\u001B[43m}\u001B[49m\n\u001B[1;32m 24\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 25\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mAPI Response:\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m 26\u001B[0m \u001B[38;5;28mprint\u001B[39m(response)\n",
|
||||||
"\n",
|
"\u001B[0;31mTypeError\u001B[0m: ApiClient.call_api() got an unexpected keyword argument 'path_params'"
|
||||||
"API Response: {'message': 'Status updated successfully'}\n",
|
|
||||||
"API Response: {'message': 'Status updated successfully'}\n"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stderr",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
|
|
||||||
" warnings.warn(\n",
|
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
|
|
||||||
" warnings.warn(\n",
|
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
|
|
||||||
" warnings.warn(\n"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 4
|
"execution_count": 43
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-02-04T13:40:09.144335Z",
|
|
||||||
"start_time": "2025-02-04T13:40:09.125904Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"# Get a list of pucks that are \"at the beamline\"\n",
|
"# Get a list of pucks that are \"at the beamline\"\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -230,42 +181,13 @@
|
|||||||
" except ApiException as e:\n",
|
" except ApiException as e:\n",
|
||||||
" print(\"Exception when calling PucksApi->get_pucks_by_slot_pucks_slot_slot_identifier_get: %s\\n\" % e)"
|
" print(\"Exception when calling PucksApi->get_pucks_by_slot_pucks_slot_slot_identifier_get: %s\\n\" % e)"
|
||||||
],
|
],
|
||||||
"id": "bbee7c94bf14000c",
|
"id": "9cf3457093751b61"
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"The response of PucksApi->get_pucks_by_slot_pucks_slot_slot_identifier_get:\n",
|
|
||||||
"\n",
|
|
||||||
"[PuckWithTellPosition(id=38, puck_name='PSIMX074', puck_type='unipuck', puck_location_in_dewar=1, dewar_id=7, dewar_name='31012025', pgroup='p20001', samples=None, tell_position=None),\n",
|
|
||||||
" PuckWithTellPosition(id=39, puck_name='PSIMX080', puck_type='unipuck', puck_location_in_dewar=2, dewar_id=7, dewar_name='31012025', pgroup='p20001', samples=None, tell_position=None),\n",
|
|
||||||
" PuckWithTellPosition(id=40, puck_name='PSIMX081', puck_type='unipuck', puck_location_in_dewar=3, dewar_id=7, dewar_name='31012025', pgroup='p20001', samples=None, tell_position=None),\n",
|
|
||||||
" PuckWithTellPosition(id=41, puck_name='PSIMX084', puck_type='unipuck', puck_location_in_dewar=4, dewar_id=7, dewar_name='31012025', pgroup='p20001', samples=None, tell_position=None),\n",
|
|
||||||
" PuckWithTellPosition(id=42, puck_name='PSIMX104', puck_type='unipuck', puck_location_in_dewar=5, dewar_id=7, dewar_name='31012025', pgroup='p20001', samples=None, tell_position=None),\n",
|
|
||||||
" PuckWithTellPosition(id=43, puck_name='PSIMX107', puck_type='unipuck', puck_location_in_dewar=6, dewar_id=7, dewar_name='31012025', pgroup='p20001', samples=None, tell_position=None),\n",
|
|
||||||
" PuckWithTellPosition(id=44, puck_name='PSIMX117', puck_type='unipuck', puck_location_in_dewar=7, dewar_id=7, dewar_name='31012025', pgroup='p20001', samples=None, tell_position=None)]\n"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stderr",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
|
|
||||||
" warnings.warn(\n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"execution_count": 52
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-02-04T13:40:49.933951Z",
|
|
||||||
"start_time": "2025-02-04T13:40:49.910479Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"from aareDBclient import SetTellPosition, SetTellPositionRequest\n",
|
"from aareDBclient import SetTellPosition, SetTellPositionRequest\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -276,23 +198,27 @@
|
|||||||
" # Payload with SetTellPosition objects\n",
|
" # Payload with SetTellPosition objects\n",
|
||||||
" payload = SetTellPositionRequest(\n",
|
" payload = SetTellPositionRequest(\n",
|
||||||
" tell=\"X06DA\",\n",
|
" tell=\"X06DA\",\n",
|
||||||
" pucks=[\n",
|
" #pucks=[\n",
|
||||||
" SetTellPosition(puck_name='PSIMX074', segment='B', puck_in_segment=1),\n",
|
" # SetTellPosition(puck_name='PSIMX074', segment='B', puck_in_segment=1),\n",
|
||||||
" SetTellPosition(puck_name='PSIMX080', segment='B', puck_in_segment=2),\n",
|
" # SetTellPosition(puck_name='PSIMX080', segment='B', puck_in_segment=2),\n",
|
||||||
" SetTellPosition(puck_name='PSIMX081', segment='C', puck_in_segment=3),\n",
|
" # SetTellPosition(puck_name='PSIMX081', segment='C', puck_in_segment=3),\n",
|
||||||
" SetTellPosition(puck_name='PSIMX084', segment='C', puck_in_segment=4),\n",
|
" # SetTellPosition(puck_name='PSIMX084', segment='C', puck_in_segment=4),\n",
|
||||||
" SetTellPosition(puck_name='PSIMX104', segment='E', puck_in_segment=5),\n",
|
" # SetTellPosition(puck_name='PSIMX104', segment='E', puck_in_segment=5),\n",
|
||||||
" SetTellPosition(puck_name='PSIMX107', segment='E', puck_in_segment=1),\n",
|
" # SetTellPosition(puck_name='PSIMX107', segment='E', puck_in_segment=1),\n",
|
||||||
" SetTellPosition(puck_name='PSIMX117', segment='F', puck_in_segment=2),\n",
|
" # SetTellPosition(puck_name='PSIMX117', segment='F', puck_in_segment=2),\n",
|
||||||
" ]\n",
|
" #]\n",
|
||||||
" #pucks=[\n",
|
" #pucks=[\n",
|
||||||
" # SetTellPosition(puck_name='PSIMX074', segment='F', puck_in_segment=1),\n",
|
" # SetTellPosition(puck_name='PSIMX074', segment='F', puck_in_segment=1),\n",
|
||||||
" # SetTellPosition(puck_name='PSIMX080', segment='F', puck_in_segment=2),\n",
|
" # SetTellPosition(puck_name='PSIMX080', segment='F', puck_in_segment=2),\n",
|
||||||
" # SetTellPosition(puck_name='PSIMX081', segment='F', puck_in_segment=3),\n",
|
|
||||||
" # SetTellPosition(puck_name='PSIMX084', segment='F', puck_in_segment=4),\n",
|
|
||||||
" # SetTellPosition(puck_name='PSIMX107', segment='A', puck_in_segment=1),\n",
|
" # SetTellPosition(puck_name='PSIMX107', segment='A', puck_in_segment=1),\n",
|
||||||
" # SetTellPosition(puck_name='PSIMX117', segment='A', puck_in_segment=2),\n",
|
" # SetTellPosition(puck_name='PSIMX117', segment='A', puck_in_segment=2),\n",
|
||||||
" #]\n",
|
" #]\n",
|
||||||
|
" pucks=[\n",
|
||||||
|
" SetTellPosition(puck_name='PK006', segment='F', puck_in_segment=1),\n",
|
||||||
|
" SetTellPosition(puck_name='PK003', segment='F', puck_in_segment=2),\n",
|
||||||
|
" SetTellPosition(puck_name='PK002', segment='A', puck_in_segment=1),\n",
|
||||||
|
" SetTellPosition(puck_name='PK001', segment='A', puck_in_segment=2),\n",
|
||||||
|
" ]\n",
|
||||||
" #pucks = []\n",
|
" #pucks = []\n",
|
||||||
" )\n",
|
" )\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -308,77 +234,13 @@
|
|||||||
" except Exception as e:\n",
|
" except Exception as e:\n",
|
||||||
" print(f\"Exception when calling PucksApi: {e}\")\n"
|
" print(f\"Exception when calling PucksApi: {e}\")\n"
|
||||||
],
|
],
|
||||||
"id": "d52d12287dd63299",
|
"id": "37e3eac6760150ee"
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"The response of PucksApi->pucks_puck_id_tell_position_put:\n",
|
|
||||||
"\n",
|
|
||||||
"[{'message': 'Tell position updated successfully.',\n",
|
|
||||||
" 'new_position': 'B1',\n",
|
|
||||||
" 'previous_position': None,\n",
|
|
||||||
" 'puck_name': 'PSIMX074',\n",
|
|
||||||
" 'status': 'updated',\n",
|
|
||||||
" 'tell': 'X06DA'},\n",
|
|
||||||
" {'message': 'Tell position updated successfully.',\n",
|
|
||||||
" 'new_position': 'B2',\n",
|
|
||||||
" 'previous_position': None,\n",
|
|
||||||
" 'puck_name': 'PSIMX080',\n",
|
|
||||||
" 'status': 'updated',\n",
|
|
||||||
" 'tell': 'X06DA'},\n",
|
|
||||||
" {'message': 'Tell position updated successfully.',\n",
|
|
||||||
" 'new_position': 'C3',\n",
|
|
||||||
" 'previous_position': None,\n",
|
|
||||||
" 'puck_name': 'PSIMX081',\n",
|
|
||||||
" 'status': 'updated',\n",
|
|
||||||
" 'tell': 'X06DA'},\n",
|
|
||||||
" {'message': 'Tell position updated successfully.',\n",
|
|
||||||
" 'new_position': 'C4',\n",
|
|
||||||
" 'previous_position': None,\n",
|
|
||||||
" 'puck_name': 'PSIMX084',\n",
|
|
||||||
" 'status': 'updated',\n",
|
|
||||||
" 'tell': 'X06DA'},\n",
|
|
||||||
" {'message': 'Tell position updated successfully.',\n",
|
|
||||||
" 'new_position': 'E5',\n",
|
|
||||||
" 'previous_position': None,\n",
|
|
||||||
" 'puck_name': 'PSIMX104',\n",
|
|
||||||
" 'status': 'updated',\n",
|
|
||||||
" 'tell': 'X06DA'},\n",
|
|
||||||
" {'message': 'Tell position updated successfully.',\n",
|
|
||||||
" 'new_position': 'E1',\n",
|
|
||||||
" 'previous_position': None,\n",
|
|
||||||
" 'puck_name': 'PSIMX107',\n",
|
|
||||||
" 'status': 'updated',\n",
|
|
||||||
" 'tell': 'X06DA'},\n",
|
|
||||||
" {'message': 'Tell position updated successfully.',\n",
|
|
||||||
" 'new_position': 'F2',\n",
|
|
||||||
" 'previous_position': None,\n",
|
|
||||||
" 'puck_name': 'PSIMX117',\n",
|
|
||||||
" 'status': 'updated',\n",
|
|
||||||
" 'tell': 'X06DA'}]\n"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stderr",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
|
|
||||||
" warnings.warn(\n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"execution_count": 55
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-02-04T13:36:46.598976Z",
|
|
||||||
"start_time": "2025-02-04T13:36:46.568865Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"# Get puck_id puck_name sample_id sample_name of pucks in the tell\n",
|
"# Get puck_id puck_name sample_id sample_name of pucks in the tell\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -389,7 +251,7 @@
|
|||||||
" # GET request: Fetch all pucks in the tell\n",
|
" # GET request: Fetch all pucks in the tell\n",
|
||||||
" try:\n",
|
" try:\n",
|
||||||
" # Call the API method to fetch pucks\n",
|
" # Call the API method to fetch pucks\n",
|
||||||
" all_pucks_response = api_instance.get_pucks_with_tell_position_pucks_with_tell_position_get()\n",
|
" all_pucks_response = api_instance.get_pucks_with_tell_position_pucks_with_tell_position_get(tell='X06DA')\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Debug response structure by printing it in JSON format\n",
|
" # Debug response structure by printing it in JSON format\n",
|
||||||
" formatted_response = json.dumps(\n",
|
" formatted_response = json.dumps(\n",
|
||||||
@ -403,49 +265,23 @@
|
|||||||
" for p in all_pucks_response:\n",
|
" for p in all_pucks_response:\n",
|
||||||
" print(f\"Puck ID: {p.id}, Puck Name: {p.puck_name}\")\n",
|
" print(f\"Puck ID: {p.id}, Puck Name: {p.puck_name}\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Check if the puck has any samples\n",
|
" ## Check if the puck has any samples\n",
|
||||||
" #if hasattr(p, 'samples') and p.samples: # Ensure 'samples' attribute exists and is not empty\n",
|
" if hasattr(p, 'samples') and p.samples: # Ensure 'samples' attribute exists and is not empty\n",
|
||||||
" # for sample in p.samples:\n",
|
" for sample in p.samples:\n",
|
||||||
" # print(f\" Sample ID: {sample.id}, Sample Name: {sample.sample_name}, Position: {sample.position}, Mount count: {sample.mount_count}\")\n",
|
" print(f\" Sample ID: {sample.id}, Sample Name: {sample.sample_name}, Position: {sample.position}, Mount count: {sample.mount_count}\")\n",
|
||||||
" #else:\n",
|
" else:\n",
|
||||||
" # print(\" No samples found in this puck.\")\n",
|
" print(\" No samples found in this puck.\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" except ApiException as e:\n",
|
" except ApiException as e:\n",
|
||||||
" print(\"Exception when calling PucksApi->get_all_pucks_in_tell: %s\\n\" % e)"
|
" print(\"Exception when calling PucksApi->get_all_pucks_in_tell: %s\\n\" % e)"
|
||||||
],
|
],
|
||||||
"id": "95f8c133359945d5",
|
"id": "51578d944878db6a"
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"Puck ID: 31, Puck Name: PSIMX074\n",
|
|
||||||
"Puck ID: 32, Puck Name: PSIMX080\n",
|
|
||||||
"Puck ID: 33, Puck Name: PSIMX081\n",
|
|
||||||
"Puck ID: 34, Puck Name: PSIMX084\n",
|
|
||||||
"Puck ID: 36, Puck Name: PSIMX107\n",
|
|
||||||
"Puck ID: 37, Puck Name: PSIMX117\n"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stderr",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
|
|
||||||
" warnings.warn(\n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"execution_count": 49
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-01-31T13:46:18.354067Z",
|
|
||||||
"start_time": "2025-01-31T13:46:18.332891Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"from aareDBclient import SampleEventCreate\n",
|
"from aareDBclient import SampleEventCreate\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -458,7 +294,7 @@
|
|||||||
" try:\n",
|
" try:\n",
|
||||||
" # Define the payload with only `event_type`\n",
|
" # Define the payload with only `event_type`\n",
|
||||||
" sample_event_create = SampleEventCreate(\n",
|
" sample_event_create = SampleEventCreate(\n",
|
||||||
" sample_id=28,\n",
|
" sample_id=58,\n",
|
||||||
" event_type=\"Mounted\" # Valid event type\n",
|
" event_type=\"Mounted\" # Valid event type\n",
|
||||||
" )\n",
|
" )\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -468,7 +304,7 @@
|
|||||||
"\n",
|
"\n",
|
||||||
" # Call the API\n",
|
" # Call the API\n",
|
||||||
" api_response = api_instance.create_sample_event_samples_samples_sample_id_events_post(\n",
|
" api_response = api_instance.create_sample_event_samples_samples_sample_id_events_post(\n",
|
||||||
" sample_id=28, # Ensure this matches a valid sample ID in the database\n",
|
" sample_id=58, # Ensure this matches a valid sample ID in the database\n",
|
||||||
" sample_event_create=sample_event_create\n",
|
" sample_event_create=sample_event_create\n",
|
||||||
" )\n",
|
" )\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -484,49 +320,13 @@
|
|||||||
" if e.body:\n",
|
" if e.body:\n",
|
||||||
" print(f\"Error Details: {e.body}\")\n"
|
" print(f\"Error Details: {e.body}\")\n"
|
||||||
],
|
],
|
||||||
"id": "ee8abb293096334a",
|
"id": "4a0665f92756b486"
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"Payload being sent to API:\n",
|
|
||||||
"{\"event_type\":\"Mounted\"}\n",
|
|
||||||
"API response:\n",
|
|
||||||
"('id', 28)\n",
|
|
||||||
"('sample_name', 'Sample028')\n",
|
|
||||||
"('position', 1)\n",
|
|
||||||
"('puck_id', 6)\n",
|
|
||||||
"('crystalname', None)\n",
|
|
||||||
"('proteinname', None)\n",
|
|
||||||
"('positioninpuck', None)\n",
|
|
||||||
"('priority', None)\n",
|
|
||||||
"('comments', None)\n",
|
|
||||||
"('data_collection_parameters', None)\n",
|
|
||||||
"('events', [SampleEventResponse(id=37, sample_id=28, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 29, 14, 3)), SampleEventResponse(id=38, sample_id=28, event_type='Unmounted', timestamp=datetime.datetime(2025, 1, 29, 14, 3, 50)), SampleEventResponse(id=408, sample_id=28, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 31, 13, 10, 3)), SampleEventResponse(id=409, sample_id=28, event_type='Unmounted', timestamp=datetime.datetime(2025, 1, 31, 13, 12, 35)), SampleEventResponse(id=410, sample_id=28, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 31, 13, 16, 55)), SampleEventResponse(id=411, sample_id=28, event_type='Unmounted', timestamp=datetime.datetime(2025, 1, 31, 13, 17, 8)), SampleEventResponse(id=412, sample_id=28, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 31, 14, 46, 18))])\n",
|
|
||||||
"('mount_count', 4)\n",
|
|
||||||
"('unmount_count', 3)\n"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stderr",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
|
|
||||||
" warnings.warn(\n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"execution_count": 11
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {},
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-01-31T12:06:44.184990Z",
|
|
||||||
"start_time": "2025-01-31T12:06:44.174766Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": null,
|
||||||
"source": [
|
"source": [
|
||||||
"### not working\n",
|
"### not working\n",
|
||||||
"with aareDBclient.ApiClient(configuration) as api_client:\n",
|
"with aareDBclient.ApiClient(configuration) as api_client:\n",
|
||||||
@ -542,141 +342,84 @@
|
|||||||
" except ApiException as e:\n",
|
" except ApiException as e:\n",
|
||||||
" print(\"Exception when calling get_last_sample_event: %s\\n\" % e)\n"
|
" print(\"Exception when calling get_last_sample_event: %s\\n\" % e)\n"
|
||||||
],
|
],
|
||||||
"id": "6a808ee09f97ae13",
|
"id": "f1d171700d6cf7fe"
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"ename": "AttributeError",
|
|
||||||
"evalue": "'SamplesApi' object has no attribute 'get_last_sample_event_samples_samples_sample_id_events_last_get'",
|
|
||||||
"output_type": "error",
|
|
||||||
"traceback": [
|
|
||||||
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
|
|
||||||
"\u001B[0;31mAttributeError\u001B[0m Traceback (most recent call last)",
|
|
||||||
"Cell \u001B[0;32mIn[6], line 7\u001B[0m\n\u001B[1;32m 3\u001B[0m api_instance \u001B[38;5;241m=\u001B[39m aareDBclient\u001B[38;5;241m.\u001B[39mSamplesApi(api_client)\n\u001B[1;32m 5\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m 6\u001B[0m \u001B[38;5;66;03m# Get the last sample event\u001B[39;00m\n\u001B[0;32m----> 7\u001B[0m last_event_response \u001B[38;5;241m=\u001B[39m \u001B[43mapi_instance\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_last_sample_event_samples_samples_sample_id_events_last_get\u001B[49m(\u001B[38;5;241m27\u001B[39m)\n\u001B[1;32m 8\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mThe response of get_last_sample_event:\u001B[39m\u001B[38;5;130;01m\\n\u001B[39;00m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m 9\u001B[0m pprint(last_event_response)\n",
|
|
||||||
"\u001B[0;31mAttributeError\u001B[0m: 'SamplesApi' object has no attribute 'get_last_sample_event_samples_samples_sample_id_events_last_get'"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"execution_count": 6
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-01-30T12:38:46.149389Z",
|
"end_time": "2025-02-26T08:45:41.872357Z",
|
||||||
"start_time": "2025-01-30T12:38:46.110767Z"
|
"start_time": "2025-02-26T08:45:41.847822Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"source": [
|
"source": [
|
||||||
"from aareDBclient import ApiClient, SamplesApi # Import the appropriate client\n",
|
"import os\n",
|
||||||
"from aareDBclient.rest import ApiException\n",
|
|
||||||
"import mimetypes\n",
|
"import mimetypes\n",
|
||||||
|
"import requests\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# File path to the image\n",
|
"# File path to the image\n",
|
||||||
"file_path = \"backend/tests/sample_image/IMG_1942.jpg\"\n",
|
"file_path = \"backend/tests/sample_image/IMG_1942.jpg\"\n",
|
||||||
|
"filename = os.path.basename(file_path)\n",
|
||||||
|
"mime_type, _ = mimetypes.guess_type(file_path)\n",
|
||||||
|
"if mime_type is None:\n",
|
||||||
|
" mime_type = \"application/octet-stream\"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# Sample ID\n",
|
"# Sample ID (ensure this exists on your backend)\n",
|
||||||
"sample_id = 27 # Replace with a valid sample_id from your FastAPI backend\n",
|
"sample_id = 58\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# Initialize the API client\n",
|
"# Build the URL for the upload endpoint.\n",
|
||||||
"with ApiClient(configuration) as api_client:\n",
|
"url = f\"https://127.0.0.1:8000/samples/{sample_id}/upload-images\"\n",
|
||||||
" api_instance = SamplesApi(api_client) # Adjust as per your API structure\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
" try:\n",
|
"# Open the file and construct the files dictionary\n",
|
||||||
" # Open the file and read as binary\n",
|
"with open(file_path, \"rb\") as file_data:\n",
|
||||||
" with open(file_path, \"rb\") as file:\n",
|
" files = {\n",
|
||||||
" # Get the MIME type for the file\n",
|
" # Use key \"uploaded_file\" as required by your API\n",
|
||||||
" mime_type, _ = mimetypes.guess_type(file_path)\n",
|
" \"uploaded_file\": (filename, file_data, mime_type)\n",
|
||||||
|
" }\n",
|
||||||
|
" headers = {\n",
|
||||||
|
" \"accept\": \"application/json\"\n",
|
||||||
|
" }\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Call the API method for uploading sample images\n",
|
" # Set verify=False to bypass certificate verification (only use in development)\n",
|
||||||
" response = api_instance.upload_sample_images_samples_samples_sample_id_upload_images_post(\n",
|
" response = requests.post(url, headers=headers, files=files, verify=False)\n",
|
||||||
" sample_id=sample_id,\n",
|
|
||||||
" uploaded_files=[file.read()] # Pass raw bytes as a list\n",
|
|
||||||
" )\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
" # Print the response from the API\n",
|
"# Check the API response\n",
|
||||||
" print(\"API Response:\")\n",
|
"print(\"API Response:\")\n",
|
||||||
" print(response)\n",
|
"print(response.status_code)\n",
|
||||||
"\n",
|
"try:\n",
|
||||||
" except ApiException as e:\n",
|
" print(response.json())\n",
|
||||||
" # Handle API exception gracefully\n",
|
"except Exception:\n",
|
||||||
" print(\"Exception occurred while uploading the file:\")\n",
|
" print(response.text)\n"
|
||||||
" print(f\"Status Code: {e.status}\")\n",
|
|
||||||
" if e.body:\n",
|
|
||||||
" print(f\"Error Details: {e.body}\")"
|
|
||||||
],
|
],
|
||||||
"id": "40404614d1a63f95",
|
"id": "11f62976d2e7d9b1",
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"ename": "ValueError",
|
|
||||||
"evalue": "Unsupported file value",
|
|
||||||
"output_type": "error",
|
|
||||||
"traceback": [
|
|
||||||
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
|
|
||||||
"\u001B[0;31mValueError\u001B[0m Traceback (most recent call last)",
|
|
||||||
"Cell \u001B[0;32mIn[19], line 22\u001B[0m\n\u001B[1;32m 19\u001B[0m mime_type, _ \u001B[38;5;241m=\u001B[39m mimetypes\u001B[38;5;241m.\u001B[39mguess_type(file_path)\n\u001B[1;32m 21\u001B[0m \u001B[38;5;66;03m# Call the API method for uploading sample images\u001B[39;00m\n\u001B[0;32m---> 22\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[43mapi_instance\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mupload_sample_images_samples_samples_sample_id_upload_images_post\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 23\u001B[0m \u001B[43m \u001B[49m\u001B[43msample_id\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msample_id\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 24\u001B[0m \u001B[43m \u001B[49m\u001B[43muploaded_files\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m[\u001B[49m\u001B[43mfile\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mread\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[43m]\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;66;43;03m# Pass raw bytes as a list\u001B[39;49;00m\n\u001B[1;32m 25\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 27\u001B[0m \u001B[38;5;66;03m# Print the response from the API\u001B[39;00m\n\u001B[1;32m 28\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mAPI Response:\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n",
|
|
||||||
"File \u001B[0;32m/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pydantic/validate_call_decorator.py:60\u001B[0m, in \u001B[0;36mvalidate_call.<locals>.validate.<locals>.wrapper_function\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 58\u001B[0m \u001B[38;5;129m@functools\u001B[39m\u001B[38;5;241m.\u001B[39mwraps(function)\n\u001B[1;32m 59\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;21mwrapper_function\u001B[39m(\u001B[38;5;241m*\u001B[39margs, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs):\n\u001B[0;32m---> 60\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mvalidate_call_wrapper\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n",
|
|
||||||
"File \u001B[0;32m/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pydantic/_internal/_validate_call.py:96\u001B[0m, in \u001B[0;36mValidateCallWrapper.__call__\u001B[0;34m(self, *args, **kwargs)\u001B[0m\n\u001B[1;32m 95\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;21m__call__\u001B[39m(\u001B[38;5;28mself\u001B[39m, \u001B[38;5;241m*\u001B[39margs: Any, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs: Any) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m Any:\n\u001B[0;32m---> 96\u001B[0m res \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m__pydantic_validator__\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mvalidate_python\u001B[49m\u001B[43m(\u001B[49m\u001B[43mpydantic_core\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mArgsKwargs\u001B[49m\u001B[43m(\u001B[49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 97\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m__return_pydantic_validator__:\n\u001B[1;32m 98\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m__return_pydantic_validator__(res)\n",
|
|
||||||
"File \u001B[0;32m~/PycharmProjects/heidi-v2/backend/aareDBclient/api/samples_api.py:874\u001B[0m, in \u001B[0;36mSamplesApi.upload_sample_images_samples_samples_sample_id_upload_images_post\u001B[0;34m(self, sample_id, uploaded_files, _request_timeout, _request_auth, _content_type, _headers, _host_index)\u001B[0m\n\u001B[1;32m 827\u001B[0m \u001B[38;5;129m@validate_call\u001B[39m\n\u001B[1;32m 828\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;21mupload_sample_images_samples_samples_sample_id_upload_images_post\u001B[39m(\n\u001B[1;32m 829\u001B[0m \u001B[38;5;28mself\u001B[39m,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 843\u001B[0m _host_index: Annotated[StrictInt, Field(ge\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m0\u001B[39m, le\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m0\u001B[39m)] \u001B[38;5;241m=\u001B[39m \u001B[38;5;241m0\u001B[39m,\n\u001B[1;32m 844\u001B[0m ) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m \u001B[38;5;28mobject\u001B[39m:\n\u001B[1;32m 845\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124;03m\"\"\"Upload Sample Images\u001B[39;00m\n\u001B[1;32m 846\u001B[0m \n\u001B[1;32m 847\u001B[0m \n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 871\u001B[0m \u001B[38;5;124;03m :return: Returns the result object.\u001B[39;00m\n\u001B[1;32m 872\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m \u001B[38;5;66;03m# noqa: E501\u001B[39;00m\n\u001B[0;32m--> 874\u001B[0m _param \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_upload_sample_images_samples_samples_sample_id_upload_images_post_serialize\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 875\u001B[0m \u001B[43m \u001B[49m\u001B[43msample_id\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msample_id\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 876\u001B[0m \u001B[43m \u001B[49m\u001B[43muploaded_files\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43muploaded_files\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 877\u001B[0m \u001B[43m \u001B[49m\u001B[43m_request_auth\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_request_auth\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 878\u001B[0m \u001B[43m \u001B[49m\u001B[43m_content_type\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_content_type\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 879\u001B[0m \u001B[43m \u001B[49m\u001B[43m_headers\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_headers\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 880\u001B[0m \u001B[43m \u001B[49m\u001B[43m_host_index\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_host_index\u001B[49m\n\u001B[1;32m 881\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 883\u001B[0m _response_types_map: Dict[\u001B[38;5;28mstr\u001B[39m, Optional[\u001B[38;5;28mstr\u001B[39m]] \u001B[38;5;241m=\u001B[39m {\n\u001B[1;32m 884\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m200\u001B[39m\u001B[38;5;124m'\u001B[39m: \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mobject\u001B[39m\u001B[38;5;124m\"\u001B[39m,\n\u001B[1;32m 885\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m422\u001B[39m\u001B[38;5;124m'\u001B[39m: \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mHTTPValidationError\u001B[39m\u001B[38;5;124m\"\u001B[39m,\n\u001B[1;32m 886\u001B[0m }\n\u001B[1;32m 887\u001B[0m response_data \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mapi_client\u001B[38;5;241m.\u001B[39mcall_api(\n\u001B[1;32m 888\u001B[0m \u001B[38;5;241m*\u001B[39m_param,\n\u001B[1;32m 889\u001B[0m _request_timeout\u001B[38;5;241m=\u001B[39m_request_timeout\n\u001B[1;32m 890\u001B[0m )\n",
|
|
||||||
"File \u001B[0;32m~/PycharmProjects/heidi-v2/backend/aareDBclient/api/samples_api.py:1096\u001B[0m, in \u001B[0;36mSamplesApi._upload_sample_images_samples_samples_sample_id_upload_images_post_serialize\u001B[0;34m(self, sample_id, uploaded_files, _request_auth, _content_type, _headers, _host_index)\u001B[0m\n\u001B[1;32m 1092\u001B[0m \u001B[38;5;66;03m# authentication setting\u001B[39;00m\n\u001B[1;32m 1093\u001B[0m _auth_settings: List[\u001B[38;5;28mstr\u001B[39m] \u001B[38;5;241m=\u001B[39m [\n\u001B[1;32m 1094\u001B[0m ]\n\u001B[0;32m-> 1096\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mapi_client\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mparam_serialize\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 1097\u001B[0m \u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43mPOST\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1098\u001B[0m \u001B[43m \u001B[49m\u001B[43mresource_path\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43m/samples/samples/\u001B[39;49m\u001B[38;5;132;43;01m{sample_id}\u001B[39;49;00m\u001B[38;5;124;43m/upload-images\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1099\u001B[0m \u001B[43m \u001B[49m\u001B[43mpath_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_path_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1100\u001B[0m \u001B[43m \u001B[49m\u001B[43mquery_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_query_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1101\u001B[0m \u001B[43m \u001B[49m\u001B[43mheader_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_header_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1102\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_body_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1103\u001B[0m \u001B[43m \u001B[49m\u001B[43mpost_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_form_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1104\u001B[0m \u001B[43m \u001B[49m\u001B[43mfiles\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_files\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1105\u001B[0m \u001B[43m \u001B[49m\u001B[43mauth_settings\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_auth_settings\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1106\u001B[0m \u001B[43m \u001B[49m\u001B[43mcollection_formats\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_collection_formats\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1107\u001B[0m \u001B[43m \u001B[49m\u001B[43m_host\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_host\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1108\u001B[0m \u001B[43m \u001B[49m\u001B[43m_request_auth\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_request_auth\u001B[49m\n\u001B[1;32m 1109\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n",
|
|
||||||
"File \u001B[0;32m~/PycharmProjects/heidi-v2/backend/aareDBclient/api_client.py:214\u001B[0m, in \u001B[0;36mApiClient.param_serialize\u001B[0;34m(self, method, resource_path, path_params, query_params, header_params, body, post_params, files, auth_settings, collection_formats, _host, _request_auth)\u001B[0m\n\u001B[1;32m 209\u001B[0m post_params \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mparameters_to_tuples(\n\u001B[1;32m 210\u001B[0m post_params,\n\u001B[1;32m 211\u001B[0m collection_formats\n\u001B[1;32m 212\u001B[0m )\n\u001B[1;32m 213\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m files:\n\u001B[0;32m--> 214\u001B[0m post_params\u001B[38;5;241m.\u001B[39mextend(\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mfiles_parameters\u001B[49m\u001B[43m(\u001B[49m\u001B[43mfiles\u001B[49m\u001B[43m)\u001B[49m)\n\u001B[1;32m 216\u001B[0m \u001B[38;5;66;03m# auth setting\u001B[39;00m\n\u001B[1;32m 217\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mupdate_params_for_auth(\n\u001B[1;32m 218\u001B[0m header_params,\n\u001B[1;32m 219\u001B[0m query_params,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 224\u001B[0m request_auth\u001B[38;5;241m=\u001B[39m_request_auth\n\u001B[1;32m 225\u001B[0m )\n",
|
|
||||||
"File \u001B[0;32m~/PycharmProjects/heidi-v2/backend/aareDBclient/api_client.py:554\u001B[0m, in \u001B[0;36mApiClient.files_parameters\u001B[0;34m(self, files)\u001B[0m\n\u001B[1;32m 552\u001B[0m filedata \u001B[38;5;241m=\u001B[39m v\n\u001B[1;32m 553\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m--> 554\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mUnsupported file value\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m 555\u001B[0m mimetype \u001B[38;5;241m=\u001B[39m (\n\u001B[1;32m 556\u001B[0m mimetypes\u001B[38;5;241m.\u001B[39mguess_type(filename)[\u001B[38;5;241m0\u001B[39m]\n\u001B[1;32m 557\u001B[0m \u001B[38;5;129;01mor\u001B[39;00m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mapplication/octet-stream\u001B[39m\u001B[38;5;124m'\u001B[39m\n\u001B[1;32m 558\u001B[0m )\n\u001B[1;32m 559\u001B[0m params\u001B[38;5;241m.\u001B[39mappend(\n\u001B[1;32m 560\u001B[0m \u001B[38;5;28mtuple\u001B[39m([k, \u001B[38;5;28mtuple\u001B[39m([filename, filedata, mimetype])])\n\u001B[1;32m 561\u001B[0m )\n",
|
|
||||||
"\u001B[0;31mValueError\u001B[0m: Unsupported file value"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"execution_count": 19
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"metadata": {
|
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-01-20T15:14:51.219091Z",
|
|
||||||
"start_time": "2025-01-20T15:14:51.216755Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cell_type": "code",
|
|
||||||
"source": "help(api_instance.upload_sample_images_samples_samples_sample_id_upload_images_post)",
|
|
||||||
"id": "8dd70634ffa5f37e",
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"Help on method upload_sample_images_samples_samples_sample_id_upload_images_post in module aareDBclient.api.samples_api:\n",
|
"API Response:\n",
|
||||||
"\n",
|
"200\n",
|
||||||
"upload_sample_images_samples_samples_sample_id_upload_images_post(sample_id: Annotated[int, Strict(strict=True)], uploaded_files: List[Union[Annotated[bytes, Strict(strict=True)], Annotated[str, Strict(strict=True)]]], _request_timeout: Union[NoneType, Annotated[float, Strict(strict=True), FieldInfo(annotation=NoneType, required=True, metadata=[Gt(gt=0)])], Tuple[Annotated[float, Strict(strict=True), FieldInfo(annotation=NoneType, required=True, metadata=[Gt(gt=0)])], Annotated[float, Strict(strict=True), FieldInfo(annotation=NoneType, required=True, metadata=[Gt(gt=0)])]]] = None, _request_auth: Optional[Dict[Annotated[str, Strict(strict=True)], Any]] = None, _content_type: Optional[Annotated[str, Strict(strict=True)]] = None, _headers: Optional[Dict[Annotated[str, Strict(strict=True)], Any]] = None, _host_index: Annotated[int, Strict(strict=True), FieldInfo(annotation=NoneType, required=True, metadata=[Ge(ge=0), Le(le=0)])] = 0) -> object method of aareDBclient.api.samples_api.SamplesApi instance\n",
|
"{'message': '1 image uploaded successfully.', 'file': 'images/p20001, p20002/2025-02-26/Dewar Two/PK001/14/IMG_1942.jpg'}\n"
|
||||||
" Upload Sample Images\n",
|
]
|
||||||
"\n",
|
},
|
||||||
" Uploads images for a sample and stores them in a directory structure: images/user/date/dewar_name/puck_name/position/. Args: sample_id (int): ID of the sample. uploaded_files (Union[List[UploadFile], List[bytes]]): List of image files (as UploadFile or raw bytes). db (Session): SQLAlchemy database session.\n",
|
{
|
||||||
"\n",
|
"name": "stderr",
|
||||||
" :param sample_id: (required)\n",
|
"output_type": "stream",
|
||||||
" :type sample_id: int\n",
|
"text": [
|
||||||
" :param uploaded_files: (required)\n",
|
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
|
||||||
" :type uploaded_files: List[bytearray]\n",
|
" warnings.warn(\n"
|
||||||
" :param _request_timeout: timeout setting for this request. If one\n",
|
|
||||||
" number provided, it will be total request\n",
|
|
||||||
" timeout. It can also be a pair (tuple) of\n",
|
|
||||||
" (connection, read) timeouts.\n",
|
|
||||||
" :type _request_timeout: int, tuple(int, int), optional\n",
|
|
||||||
" :param _request_auth: set to override the auth_settings for an a single\n",
|
|
||||||
" request; this effectively ignores the\n",
|
|
||||||
" authentication in the spec for a single request.\n",
|
|
||||||
" :type _request_auth: dict, optional\n",
|
|
||||||
" :param _content_type: force content-type for the request.\n",
|
|
||||||
" :type _content_type: str, Optional\n",
|
|
||||||
" :param _headers: set to override the headers for a single\n",
|
|
||||||
" request; this effectively ignores the headers\n",
|
|
||||||
" in the spec for a single request.\n",
|
|
||||||
" :type _headers: dict, optional\n",
|
|
||||||
" :param _host_index: set to override the host_index for a single\n",
|
|
||||||
" request; this effectively ignores the host_index\n",
|
|
||||||
" in the spec for a single request.\n",
|
|
||||||
" :type _host_index: int, optional\n",
|
|
||||||
" :return: Returns the result object.\n",
|
|
||||||
"\n"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 51
|
"execution_count": 70
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadata": {},
|
||||||
|
"cell_type": "code",
|
||||||
|
"outputs": [],
|
||||||
|
"execution_count": null,
|
||||||
|
"source": "help(api_instance.upload_sample_images_samples_samples_sample_id_upload_images_post)",
|
||||||
|
"id": "cb1b99e6327fff84"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user