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:
GotthardG 2025-02-26 09:58:19 +01:00
parent 43d67b1044
commit f588bc0cda
13 changed files with 360 additions and 418 deletions

View File

@ -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",
] ]

View File

@ -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(

View File

@ -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()

View File

@ -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"
# #

View File

@ -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",

View File

@ -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}

View 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

View File

@ -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

View File

@ -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"]
) )

View File

@ -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),
} }

View File

@ -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]

View File

@ -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"]

View File

@ -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": {