Skip to content

Commit

Permalink
Merge pull request #141 from traP-jp/feat/#140-separate-py-frontend-a…
Browse files Browse the repository at this point in the history
…nd-logic

🎨 separate python api and logic
  • Loading branch information
comavius authored Feb 24, 2025
2 parents b3ecbde + 9707daa commit 3550774
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 118 deletions.
1 change: 1 addition & 0 deletions lib/judge_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod job;
pub mod judge_output;
pub mod problem_registry;
pub mod procedure;
pub mod procedure_builder;
pub mod registered_procedure_converter;
pub mod runner;
pub mod writer_schema_transpiler;
151 changes: 151 additions & 0 deletions lib/judge_core/src/procedure_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use crate::procedure::writer_schema::*;
use std::collections::HashMap;

#[derive(Debug, Clone)]
enum Job {
Resource(ResourceKind),
Execution(Execution),
Script(Text),
}

/// Internal builder to create a judge-procedure
#[derive(Debug, Clone)]
pub struct ProcedureBuilder {
jobs: HashMap<String, Job>,
}

impl From<ProcedureBuilder> for Procedure {
fn from(builder: ProcedureBuilder) -> Self {
Self {
resources: builder
.jobs
.iter()
.filter_map(|(_, job)| match job {
Job::Resource(resource) => Some(resource.clone()),
_ => None,
})
.collect(),
executions: builder
.jobs
.iter()
.filter_map(|(_, job)| match job {
Job::Execution(execution) => Some(execution.clone()),
_ => None,
})
.collect(),
scripts: builder
.jobs
.iter()
.filter_map(|(_, job)| match job {
Job::Script(script) => Some(script.clone()),
_ => None,
})
.collect(),
}
}
}

impl ProcedureBuilder {
pub fn new() -> Self {
ProcedureBuilder {
jobs: HashMap::new(),
}
}

/// Add a resource to the procedure and return the name of the resource
pub fn add_resource(&mut self, resource: ResourceKind) -> Result<String, AddJobError> {
let name = match &resource {
ResourceKind::EmptyDirectory(empty_directory) => empty_directory.name.clone(),
ResourceKind::RuntimeTextFile(runtime_text) => runtime_text.name.clone(),
ResourceKind::TextFile(text) => text.name.clone(),
};
self.jobs
.insert(name.clone(), Job::Resource(resource))
.map_or_else(
|| Ok(name.clone()),
|_| Err(AddJobError::ResourceAlreadyExists(name.clone())),
)?;
Ok(name)
}

/// Add a script to the procedure and return the name of the script
pub fn add_script(&mut self, script: Text) -> Result<String, AddJobError> {
let name = script.name.clone();
self.jobs
.insert(name.clone(), Job::Script(script))
.map_or_else(
|| Ok(name.clone()),
|_| Err(AddJobError::ResourceAlreadyExists(name.clone())),
)?;
Ok(name)
}

/// Add an execution to the procedure and return the name of the execution
pub fn add_execution(&mut self, execution: Execution) -> Result<String, AddJobError> {
let name = execution.name.clone();
// Check if all dependencies are present
for dep in execution.depends_on.iter() {
self.jobs
.get(&dep.ref_to)
.ok_or(AddJobError::DependencyNotFound(dep.ref_to.clone()))?;
}
self.jobs
.get(&execution.script_name)
.ok_or(AddJobError::DependencyNotFound(
execution.script_name.clone(),
))?;
// Insert the execution
let _ = self
.jobs
.insert(name.clone(), Job::Execution(execution))
.map_or_else(
|| Ok(name.clone()),
|_| Err(AddJobError::ResourceAlreadyExists(name.clone())),
)?;
Ok(name)
}

/// Export the procedure
pub fn get_procedure(&self) -> Procedure {
Procedure {
resources: self
.jobs
.iter()
.filter_map(|(_, job)| match job {
Job::Resource(resource) => Some(resource.clone()),
_ => None,
})
.collect(),
executions: self
.jobs
.iter()
.filter_map(|(_, job)| match job {
Job::Execution(execution) => Some(execution.clone()),
_ => None,
})
.collect(),
scripts: self
.jobs
.iter()
.filter_map(|(_, job)| match job {
Job::Script(script) => Some(script.clone()),
_ => None,
})
.collect(),
}
}
}

#[derive(Clone, Debug, thiserror::Error)]
pub enum AddJobError {
#[error("Name {0} already exists")]
ResourceAlreadyExists(String),
#[error("Dependency {0} not found")]
DependencyNotFound(String),
}

#[derive(Clone, Debug, thiserror::Error)]
pub enum SchemaSerializationError {
#[error("Failed to serialize schema: {0}")]
SerializationError(String),
}
4 changes: 2 additions & 2 deletions pylib/traopy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use pyo3::prelude::*;
use pyo3_stub_gen::define_stub_info_gatherer;
pub mod local_judge;
pub mod models;
pub mod procedure;
pub mod procedure_builder;

#[pymodule]
fn lowlevel(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<procedure::PyProcedure>()?;
m.add_class::<procedure_builder::PyProcedureBuilder>()?;
m.add_class::<models::dependency::PyDependency>()?;
m.add_class::<models::empty_directory::PyEmptyDirectory>()?;
m.add_class::<models::execution::PyExecution>()?;
Expand Down
6 changes: 3 additions & 3 deletions pylib/traopy/src/local_judge.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::procedure::PyProcedure;
use crate::procedure_builder::PyProcedureBuilder;
use judge_core::{
problem_registry::ProblemRegistryClient as _, problem_registry::ProblemRegistryServer as _, *,
};
Expand Down Expand Up @@ -34,10 +34,10 @@ impl LocalJudge {
#[pyo3(name = "run")]
async fn run(
&self,
procedure: PyProcedure,
builder: PyProcedureBuilder,
runtime_text_contents: HashMap<String, String>,
) -> PyResult<String> {
let procedure: procedure::writer_schema::Procedure = procedure.into();
let procedure: procedure::writer_schema::Procedure = builder.get_schema_procedure();
let registered_procedure = self.registry_server.register(procedure).await.unwrap();
let (runtime_procedure, runtime_id_to_dep_id) =
registered_procedure_converter::convert(&registered_procedure, &runtime_text_contents)
Expand Down
113 changes: 0 additions & 113 deletions pylib/traopy/src/procedure.rs

This file was deleted.

71 changes: 71 additions & 0 deletions pylib/traopy/src/procedure_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use crate::models::*;
use judge_core::procedure::writer_schema;
use judge_core::procedure_builder;
use pyo3::prelude::*;
use pyo3_stub_gen::derive::*;
use std::path::PathBuf;

#[gen_stub_pyclass]
#[pyclass]
#[derive(Debug, Clone)]
pub struct PyProcedureBuilder {
builder: procedure_builder::ProcedureBuilder,
}

#[gen_stub_pymethods]
#[pymethods]
impl PyProcedureBuilder {
#[new]
fn new() -> Self {
PyProcedureBuilder {
builder: procedure_builder::ProcedureBuilder::new(),
}
}

#[pyo3(name = "add_resource")]
fn add_resource(&mut self, resource: resource_kind::PyResourceKind) -> output::PyOutput {
let schema_resource = writer_schema::ResourceKind::from(resource);
let name = self.builder.add_resource(schema_resource).unwrap();
let output = output::PyOutput::new(name);
output
}

#[pyo3(name = "add_script")]
fn add_script(&mut self, script: text::PyText) -> output::PyScriptOutput {
let schema_script = writer_schema::Text::from(script);
let name = self.builder.add_script(schema_script).unwrap();
let output = output::PyScriptOutput::new(name);
output
}

#[pyo3(name = "add_execution")]
fn add_execution(&mut self, execution: execution::PyExecution) -> output::PyOutput {
let script_name = execution.script.name.clone();
let dependencies = execution
.depends_on
.iter()
.map(|dep: &dependency::PyDependency| {
let schema_dep = writer_schema::Dependency::from(dep.clone());
schema_dep
})
.collect::<Vec<_>>();
let schema_execution = execution::new_execution(execution.name, script_name, dependencies);
let name = self.builder.add_execution(schema_execution).unwrap();
let output = output::PyOutput::new(name);
output
}

#[pyo3(name = "write_to")]
fn write_to(&self, path: PathBuf) -> () {
// output this instance as a json file
let serializable = self.builder.get_procedure();
let json = serde_json::to_string(&serializable).unwrap();
std::fs::write(path, json).unwrap();
}
}

impl PyProcedureBuilder {
pub fn get_schema_procedure(&self) -> writer_schema::Procedure {
self.builder.get_procedure()
}
}

0 comments on commit 3550774

Please sign in to comment.