Data Models¶
SPIDB extends the SONICDB framework with specialized models for stored product insect research. All models use SQLAlchemy ORM and support SQLite, MySQL, and PostgreSQL databases.
Core Models (from SONICDB)¶
Channel¶
Represents an individual recording channel.
Attributes:
id(Integer) - Primary keynumber(Integer) - Channel numbertype_class(String) - Channel type (e.g., “piezoelectric”, “microwave”)sensor_id(Integer) - Foreign key to Sensor
Relationships:
sensor- Parent Sensor objectfiles- List of File objects from this channelsamples- List of Sample objects from this channel
Example:
# Get all piezoelectric channels
piezo_channels = db.session.query(db.Channel).filter(
db.Channel.type_class == "piezoelectric"
).all()
File¶
Stores audio file metadata and locations.
Attributes:
id(Integer) - Primary keyfilepath(String) - Absolute path to filefilename(String) - File nameextension(String) - File extension (e.g., “wav”)sample_rate(Integer) - Sample rate in Hzstart(DateTime) - Recording start timeend(DateTime) - Recording end timeduration(Float) - Duration in secondschannel_number(Integer) - Channel numberchannel_id(Integer) - Foreign key to Channelsensor_id(Integer) - Foreign key to Sensor
Relationships:
channel- Parent Channel objectsensor- Parent Sensor objectsamples- List of Sample objects using this file
Example:
# Get all WAV files
wav_files = db.session.query(db.File).filter(
db.File.extension == "wav"
).all()
# Get files from a specific channel
channel_files = db.session.query(db.File).filter(
db.File.channel_number == 0
).all()
Sensor¶
Represents recording equipment.
Attributes:
id(Integer) - Primary keyname(String) - Sensor name (e.g., “A-SPIDS”)subname(String) - Version or variant (e.g., “v1.0”)manufacturer(String) - Manufacturer namenumber_of_channels(Integer) - Number of recording channelstype_class(String) - Sensor type (e.g., “multi-sensor”)
Relationships:
channels- List of Channel objectsevents- List of Event objectsrecords- List of Record objectssamples- List of Sample objectsfiles- List of File objects
Example:
sensor = db.session.query(db.Sensor).filter(
db.Sensor.name == "A-SPIDS"
).first()
print(f"{sensor.name} has {sensor.number_of_channels} channels")
Extended Models (SPIDB-specific)¶
Event¶
Represents a recording session with specific subject, material, and noise conditions.
Attributes:
id(Integer) - Primary keystart(DateTime) - Event start timeend(DateTime) - Event end timedescription(String) - Text descriptionnoise(String) - Noise level/type (e.g., “Silence”, “60dBA”, “Ambient”)sensor_id(Integer) - Foreign key to Sensorsubject_id(Integer) - Foreign key to Subjectmaterial_id(Integer) - Foreign key to Material
Relationships:
sensor- Parent Sensor objectsubject- Associated Subject objectmaterial- Associated Material objectrecords- List of Record objects from this eventsamples- List of Sample objects from this event
Example:
from datetime import datetime
# Create a new event
event = db.Event(
start=datetime(2023, 5, 24, 19, 41, 19),
end=datetime(2023, 5, 24, 19, 51, 19),
description="Single cowpea beetle in rice",
noise="Silence",
sensor=sensor,
subject=subject,
material=material
)
db.session.add(event)
db.session.commit()
# Query events by noise level
silent_events = db.session.query(db.Event).filter(
db.Event.noise == "Silence"
).all()
Material¶
Represents substrate materials (grains, seeds, etc.).
Attributes:
id(Integer) - Primary keyname(String) - Material identifierscientific_name(String) - Taxonomic name (for organic materials)common_name(String) - Common namedensity(Float) - Material density in g/cm³
Relationships:
events- List of Event objects with this materialrecords- List of Record objects with this materialsamples- List of Sample objects with this material
Example:
# Create a new material
material = db.Material(
name="rice",
scientific_name="Oryza sativa",
common_name="Rice",
density=1.4
)
db.session.add(material)
db.session.commit()
# Get all samples in rice
rice_samples = db.session.query(db.Sample).join(db.Material).filter(
db.Material.name == "rice"
).all()
Record¶
Represents fixed-duration segments of events (typically 60 seconds).
Attributes:
id(Integer) - Primary keystart(DateTime) - Record start timeend(DateTime) - Record end timeevent_id(Integer) - Foreign key to parent Eventsensor_id(Integer) - Foreign key to Sensorsubject_id(Integer) - Foreign key to Subjectmaterial_id(Integer) - Foreign key to Materialnoise(String) - Noise level (inherited from event)
Relationships:
event- Parent Event objectsensor- Associated Sensor objectsubject- Associated Subject objectmaterial- Associated Material objectsamples- List of Sample objects from this record
Example:
# Generate 60-second records from events
from spidb.build import generate_records
generate_records(db, duration=60, overwrite=True)
# Query records for a specific subject
cowpea_records = db.session.query(db.Record).join(db.Subject).filter(
db.Subject.scientific_name == "Callosobruchus maculatus"
).all()
Sample¶
Represents individual channel recordings, linking audio files to experimental context.
Attributes:
id(Integer) - Primary keydatetime(DateTime) - Sample timestampevent_id(Integer) - Foreign key to Eventrecord_id(Integer) - Foreign key to Recordsensor_id(Integer) - Foreign key to Sensorchannel_id(Integer) - Foreign key to Channelsubject_id(Integer) - Foreign key to Subjectmaterial_id(Integer) - Foreign key to Materialfile_id(Integer) - Foreign key to Filenoise(String) - Noise level
Relationships:
event- Parent Event objectrecord- Parent Record objectsensor- Associated Sensor objectchannel- Associated Channel objectsubject- Associated Subject objectmaterial- Associated Material objectfile- Associated File object containing audio data
Example:
# Generate samples from records
from spidb.build import generate_samples
generate_samples(db, overwrite=True)
# Complex query: Get all samples of adult beetles in rice from channel 0
samples = db.session.query(db.Sample).join(
db.Subject
).join(
db.Material
).join(
db.Channel
).filter(
db.Subject.life_stage == "Adult",
db.Material.name == "rice",
db.Channel.number == 0
).all()
# Access related data
for sample in samples[:5]:
print(f"Subject: {sample.subject.common_name}")
print(f"Material: {sample.material.common_name}")
print(f"File: {sample.file.filename}")
print(f"Duration: {sample.file.duration}s")
print("---")
Subject¶
Represents insect specimens with biological measurements.
Attributes:
id(Integer) - Primary keyname(String) - Descriptive identifierscientific_name(String) - Taxonomic binomial namecommon_name(String) - Common/vernacular namelife_stage(String) - Development stage (e.g., “Adult”, “Larva”, “Pupa”)length(Float) - Length in millimeterswidth(Float) - Width in millimetersheight(Float) - Height in millimetersweight(Float) - Mass in gramsvolume(Float) - Volume in cubic centimetersdensity(Float) - Density in g/cm³
Relationships:
events- List of Event objects with this subjectrecords- List of Record objects with this subjectsamples- List of Sample objects with this subject
Example:
# Create a new subject
subject = db.Subject(
name="Cowpea Beetle Adult",
scientific_name="Callosobruchus maculatus",
common_name="Cowpea Beetle",
life_stage="Adult",
length=3.5,
width=1.5,
height=1.5,
weight=0.003
)
db.session.add(subject)
db.session.commit()
# Query by life stage
adults = db.session.query(db.Subject).filter(
db.Subject.life_stage == "Adult"
).all()
Common Query Patterns¶
Getting All Subjects and Their Materials¶
from sqlalchemy import distinct
# Get all subject-material combinations
combinations = db.session.query(
db.Subject.common_name,
db.Material.common_name
).join(
db.Sample
).join(
db.Material
).distinct().all()
for subject, material in combinations:
print(f"{subject} in {material}")
Counting Samples by Life Stage¶
from sqlalchemy import func
counts = db.session.query(
db.Subject.life_stage,
func.count(db.Sample.id)
).join(
db.Sample
).group_by(
db.Subject.life_stage
).all()
for stage, count in counts:
print(f"{stage}: {count} samples")
Getting Events with Duration¶
# Get all events with their durations
events = db.session.query(db.Event).all()
for event in events:
duration = (event.end - event.start).total_seconds()
print(f"{event.description}: {duration}s")
Model Relationships Diagram¶
┌─────────┐
│ Sensor │
└────┬────┘
│
├──────────┐
│ │
┌────▼────┐ ┌──▼───┐
│ Channel │ │ Event│◄──┐
└────┬────┘ └──┬───┘ │
│ │ │
┌────▼────┐ │ ┌───┴────┐ ┌──────────┐
│ File │ │ │ Subject│ │ Material │
└────┬────┘ │ └───┬────┘ └────┬─────┘
│ │ │ │
│ ┌───▼────┐ │ │
│ │ Record │◄──┴─────────────┘
│ └───┬────┘
│ │
│ ┌────▼────┐
└───► Sample │
└─────────┘
See Also¶
Database Structure - Complete database documentation
Usage Examples - Query examples and code snippets
CLI Guide - Command-line interface reference