use std::collections::HashMap;
use pyo3::prelude::*;
use crate::proto::plugin::{
self as proto, ResidueTermScores as ProtoResidueTermScores, ScoreReport,
};
use crate::protocol::PollOutcome as RustPollOutcome;
#[pyclass(
name = "BonusContribution",
module = "foldit_plugin_sdk",
from_py_object
)]
#[derive(Clone)]
pub struct PyBonusContribution {
inner: proto::BonusContribution,
}
#[pymethods]
impl PyBonusContribution {
#[new]
#[must_use]
pub fn new(kind: String, value: f32) -> Self {
Self {
inner: proto::BonusContribution { kind, value },
}
}
#[must_use]
#[getter]
pub fn kind(&self) -> String {
self.inner.kind.clone()
}
#[must_use]
#[getter]
pub fn value(&self) -> f32 {
self.inner.value
}
}
#[pyclass(
name = "ResidueTermScores",
module = "foldit_plugin_sdk",
from_py_object
)]
#[derive(Clone)]
pub struct PyResidueTermScores {
inner: ProtoResidueTermScores,
}
#[pymethods]
impl PyResidueTermScores {
#[new]
#[must_use]
pub fn new(entity_id: u64, residue_index: u32, terms: Vec<f32>) -> Self {
Self {
inner: ProtoResidueTermScores {
residue: Some(proto::ResidueRef {
entity_id,
residue_index,
}),
terms,
},
}
}
#[must_use]
#[getter]
pub fn entity_id(&self) -> Option<u64> {
self.inner.residue.as_ref().map(|r| r.entity_id)
}
#[must_use]
#[getter]
pub fn residue_index(&self) -> Option<u32> {
self.inner.residue.as_ref().map(|r| r.residue_index)
}
#[must_use]
#[getter]
pub fn terms(&self) -> Vec<f32> {
self.inner.terms.clone()
}
}
#[pyclass(name = "ScoreReport", module = "foldit_plugin_sdk", from_py_object)]
#[derive(Clone)]
pub struct PyScoreReport {
pub(crate) inner: ScoreReport,
}
#[pymethods]
impl PyScoreReport {
#[new]
#[pyo3(signature = (term_names, whole_pose_terms, per_residue_terms, bonus_breakdown = Vec::new()))]
#[must_use]
pub fn new(
term_names: Vec<String>,
whole_pose_terms: Vec<f32>,
per_residue_terms: Vec<PyResidueTermScores>,
bonus_breakdown: Vec<PyBonusContribution>,
) -> Self {
Self {
inner: ScoreReport {
term_names,
whole_pose_terms,
per_residue_terms: per_residue_terms.into_iter().map(|r| r.inner).collect(),
bonus_breakdown: bonus_breakdown.into_iter().map(|b| b.inner).collect(),
},
}
}
#[must_use]
#[getter]
pub fn term_names(&self) -> Vec<String> {
self.inner.term_names.clone()
}
#[must_use]
#[getter]
pub fn whole_pose_terms(&self) -> Vec<f32> {
self.inner.whole_pose_terms.clone()
}
#[must_use]
#[getter]
pub fn per_residue_terms(&self) -> Vec<PyResidueTermScores> {
self.inner
.per_residue_terms
.iter()
.cloned()
.map(|inner| PyResidueTermScores { inner })
.collect()
}
#[must_use]
#[getter]
pub fn bonus_breakdown(&self) -> Vec<PyBonusContribution> {
self.inner
.bonus_breakdown
.iter()
.cloned()
.map(|inner| PyBonusContribution { inner })
.collect()
}
}
#[pyclass(name = "PollOutcome", module = "foldit_plugin_sdk", from_py_object)]
#[derive(Clone)]
pub struct PyPollOutcome {
inner: RustPollOutcome,
}
#[pymethods]
impl PyPollOutcome {
#[staticmethod]
#[pyo3(signature = (latest_assembly = None, progress = None, stage = None, score = None))]
#[must_use]
pub fn pending(
latest_assembly: Option<Vec<u8>>,
progress: Option<f32>,
stage: Option<String>,
score: Option<PyScoreReport>,
) -> Self {
Self {
inner: RustPollOutcome::Pending {
latest_assembly,
progress,
stage,
score: score.map(|s| s.inner),
},
}
}
#[staticmethod]
#[pyo3(signature = (latest_assembly = None, progress = None, stage = None, score = None))]
#[must_use]
pub fn checkpoint(
latest_assembly: Option<Vec<u8>>,
progress: Option<f32>,
stage: Option<String>,
score: Option<PyScoreReport>,
) -> Self {
Self {
inner: RustPollOutcome::Checkpoint {
latest_assembly,
progress,
stage,
score: score.map(|s| s.inner),
},
}
}
#[staticmethod]
#[pyo3(signature = (assembly, score = None))]
#[must_use]
pub fn cancelled(assembly: Vec<u8>, score: Option<PyScoreReport>) -> Self {
Self {
inner: RustPollOutcome::Cancelled {
assembly,
score: score.map(|s| s.inner),
},
}
}
#[staticmethod]
#[pyo3(signature = (assembly, score = None))]
#[must_use]
pub fn final_(assembly: Vec<u8>, score: Option<PyScoreReport>) -> Self {
Self {
inner: RustPollOutcome::Final {
assembly,
score: score.map(|s| s.inner),
},
}
}
#[staticmethod]
#[must_use]
pub fn error(code: String, message: String, details: HashMap<String, String>) -> Self {
Self {
inner: RustPollOutcome::Error {
code,
message,
details,
},
}
}
#[must_use]
#[getter]
pub fn kind(&self) -> &'static str {
match self.inner {
RustPollOutcome::Pending { .. } => "pending",
RustPollOutcome::Checkpoint { .. } => "checkpoint",
RustPollOutcome::Cancelled { .. } => "cancelled",
RustPollOutcome::Final { .. } => "final",
RustPollOutcome::Error { .. } => "error",
}
}
#[must_use]
#[getter]
pub fn assembly(&self) -> Option<Vec<u8>> {
match &self.inner {
RustPollOutcome::Pending {
latest_assembly, ..
}
| RustPollOutcome::Checkpoint {
latest_assembly, ..
} => latest_assembly.clone(),
RustPollOutcome::Cancelled { assembly, .. }
| RustPollOutcome::Final { assembly, .. } => Some(assembly.clone()),
RustPollOutcome::Error { .. } => None,
}
}
#[must_use]
#[getter]
pub fn progress(&self) -> Option<f32> {
match self.inner {
RustPollOutcome::Pending { progress, .. }
| RustPollOutcome::Checkpoint { progress, .. } => progress,
_ => None,
}
}
#[must_use]
#[getter]
pub fn stage(&self) -> Option<String> {
match &self.inner {
RustPollOutcome::Pending { stage, .. } | RustPollOutcome::Checkpoint { stage, .. } => {
stage.clone()
}
_ => None,
}
}
#[must_use]
#[getter]
pub fn score(&self) -> Option<PyScoreReport> {
let report = match &self.inner {
RustPollOutcome::Pending { score, .. }
| RustPollOutcome::Checkpoint { score, .. }
| RustPollOutcome::Cancelled { score, .. }
| RustPollOutcome::Final { score, .. } => score.clone(),
RustPollOutcome::Error { .. } => None,
};
report.map(|inner| PyScoreReport { inner })
}
#[must_use]
#[getter]
pub fn error_code(&self) -> Option<String> {
match &self.inner {
RustPollOutcome::Error { code, .. } => Some(code.clone()),
_ => None,
}
}
#[must_use]
#[getter]
pub fn error_message(&self) -> Option<String> {
match &self.inner {
RustPollOutcome::Error { message, .. } => Some(message.clone()),
_ => None,
}
}
#[must_use]
#[getter]
pub fn error_details(&self) -> HashMap<String, String> {
match &self.inner {
RustPollOutcome::Error { details, .. } => details.clone(),
_ => HashMap::new(),
}
}
}
impl PyPollOutcome {
#[must_use]
pub fn into_inner(self) -> RustPollOutcome {
self.inner
}
}