Skip to content

Commit

Permalink
Merge pull request #123 from traP-jp/feat/#122-local-problem-registry
Browse files Browse the repository at this point in the history
Feat/#122 local problem registry
  • Loading branch information
comavius authored Feb 18, 2025
2 parents 458f1bf + 4cdfdfd commit 0481e5f
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 13 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ pyo3-stub-gen = "0.7.0"
members = [
"lib/jobapi",
"lib/local_jobapi",
"lib/local_problem_registry",
"lib/grpc_schema",
"lib/judge_core",
"pylib/traopy",
"app/judge_control_app",
"app/judge_control_app", "lib/local_problem_registry",
]
resolver = "2"
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,3 +3,4 @@ pub mod job;
pub mod problem_registry;
pub mod procedure;
pub mod runner;
pub mod writer_schema_transpiler;
12 changes: 10 additions & 2 deletions lib/judge_core/src/problem_registry.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use crate::{identifiers::ResourceId, procedure::*};
use anyhow::Result;
use futures::Future;

/// ProblemRegistryServer uploads contents of problems to the registry in webservice-backend server.
pub trait ProblemRegistryServer {
// Memo: use crate::writer_schema_transpiler::transpile as the core logic
fn register(
&self,
problem: writer_schema::Procedure,
) -> impl Future<Output = Result<registered::Procedure>>;
) -> impl Future<Output = Result<registered::Procedure, RegistrationError>>;
}

/// ProblemRegistryClient fetches contents of problems from the registry in judge server.
Expand All @@ -25,3 +25,11 @@ pub enum ResourceFetchError {
#[error("Resource {0} not found")]
NotFound(ResourceId),
}

#[derive(Debug, Clone, thiserror::Error)]
pub enum RegistrationError {
#[error("Internal error while registering a problem: {0}")]
InternalError(String),
#[error("Invalid problem procedure schema: {0}")]
InvalidSchema(String),
}
2 changes: 1 addition & 1 deletion lib/judge_core/src/procedure/registered.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub struct Procedure {
}

pub struct RuntimeText {
pub name: String,
pub label: String,
pub dep_id: DepId,
}

Expand Down
8 changes: 4 additions & 4 deletions lib/judge_core/src/procedure/writer_schema.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Procedure {
pub resources: HashMap<String, ResourceKind>,
pub executions: HashMap<String, Execution>,
pub scripts: HashMap<String, Script>,
pub resources: Vec<ResourceKind>,
pub executions: Vec<Execution>,
pub scripts: Vec<Script>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -36,6 +35,7 @@ pub enum ResourceKind {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RuntimeText {
pub name: String,
pub label: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down
141 changes: 141 additions & 0 deletions lib/judge_core/src/writer_schema_transpiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#![allow(unused_variables)]

use crate::{
problem_registry::*,
procedure::{writer_schema::*, *},
*,
};
use std::collections::HashMap;

pub fn transpile(
problem: writer_schema::Procedure,
) -> Result<
(
registered::Procedure,
HashMap<identifiers::ResourceId, String>,
),
RegistrationError,
> {
let mut name_to_id = HashMap::new();
for resource in problem.resources.iter() {
let name = match resource {
ResourceKind::TextFile(content) => content.name.clone(),
ResourceKind::EmptyDirectory(empty_dir) => empty_dir.name.clone(),
ResourceKind::RuntimeTextFile(runtime_text) => runtime_text.name.clone(),
};
let id = identifiers::DepId::new();
name_to_id.insert(name.clone(), id);
}
for script in problem.scripts.iter() {
let name = script.name.clone();
let id = identifiers::DepId::new();
name_to_id.insert(name.clone(), id);
}
for execution in problem.executions.iter() {
let name = execution.name.clone();
let id = identifiers::DepId::new();
name_to_id.insert(name.clone(), id);
}
let mut content_to_id = HashMap::new();
for resource in problem.resources.iter() {
if let ResourceKind::TextFile(content) = resource {
let id = identifiers::ResourceId::new();
content_to_id.insert(content.content.clone(), id);
}
}
let mut runtime_texts = Vec::new();
let mut texts = Vec::new();
let mut empty_directories = Vec::new();
for resource in problem.resources.iter() {
match resource {
ResourceKind::TextFile(content) => {
let dep_id = name_to_id
.get(&content.name)
.ok_or(RegistrationError::InvalidSchema(
"TextFile name not found".to_string(),
))?
.clone();
let text = registered::Text {
resource_id: content_to_id
.get(&content.content)
.ok_or(RegistrationError::InvalidSchema(
"TextFile content not found".to_string(),
))?
.clone(),
dep_id: dep_id.clone(),
};
texts.push(text);
}
ResourceKind::EmptyDirectory(empty_dir) => {
let dep_id = name_to_id
.get(&empty_dir.name)
.ok_or(RegistrationError::InvalidSchema(
"EmptyDirectory name not found".to_string(),
))?
.clone();
let empty_directory = registered::EmptyDirectory {
dep_id: dep_id.clone(),
};
empty_directories.push(empty_directory);
}
ResourceKind::RuntimeTextFile(runtime_text) => {
let dep_id = name_to_id
.get(&runtime_text.name)
.ok_or(RegistrationError::InvalidSchema(
"RuntimeText name not found".to_string(),
))?
.clone();
let runtime_text = registered::RuntimeText {
label: runtime_text.label.clone(),
dep_id: dep_id.clone(),
};
runtime_texts.push(runtime_text);
}
}
}
let mut executions = Vec::new();
for execution in problem.executions.iter() {
let script = execution.script_name.clone();
let mut dependencies = Vec::new();
for dep in execution.depends_on.iter() {
let dep_id = name_to_id
.get(&dep.ref_to)
.ok_or(RegistrationError::InvalidSchema(
"Dependency name not found".to_string(),
))?
.clone();
let depends_on = registered::DependsOn {
dep_id: dep_id.clone(),
envvar_name: dep.envvar_name.clone(),
};
dependencies.push(depends_on);
}
let dep_id = name_to_id
.get(&execution.name)
.ok_or(RegistrationError::InvalidSchema(
"Execution name not found".to_string(),
))?
.clone();
let priority = 0;
let execution = registered::Execution {
script: script,
depends_on: dependencies,
dep_id: dep_id,
priority: priority,
};
executions.push(execution);
}
let procedure = registered::Procedure {
runtime_texts,
texts,
empty_directories,
executions,
};
Ok((
procedure,
content_to_id
.iter()
.map(|(k, v)| (v.clone(), k.clone()))
.collect(),
))
}
9 changes: 9 additions & 0 deletions lib/local_problem_registry/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "local_problem_registry"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { workspace = true }
futures = { workspace = true }
judge_core = { path = "../judge_core" }
14 changes: 14 additions & 0 deletions lib/local_problem_registry/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub mod registry_client;
pub mod registry_server;

pub fn new_registry() -> (
registry_server::RegistryServer,
registry_client::RegistryClient,
) {
let registry = std::sync::Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new()));
let server = registry_server::RegistryServer {
registry: registry.clone(),
};
let client = registry_client::RegistryClient { registry };
(server, client)
}
21 changes: 21 additions & 0 deletions lib/local_problem_registry/src/registry_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use judge_core::*;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;

pub struct RegistryClient {
pub(crate) registry: Arc<Mutex<HashMap<identifiers::ResourceId, String>>>,
}

impl problem_registry::ProblemRegistryClient for RegistryClient {
async fn fetch(
&self,
resource_id: identifiers::ResourceId,
) -> Result<String, problem_registry::ResourceFetchError> {
let registry = self.registry.lock().await;
match registry.get(&resource_id) {
Some(contents) => Ok(contents.clone()),
None => Err(problem_registry::ResourceFetchError::NotFound(resource_id)),
}
}
}
22 changes: 22 additions & 0 deletions lib/local_problem_registry/src/registry_server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use judge_core::{problem_registry::*, procedure::*, writer_schema_transpiler::transpile, *};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;

pub struct RegistryServer {
pub(crate) registry: Arc<Mutex<HashMap<identifiers::ResourceId, String>>>,
}

impl ProblemRegistryServer for RegistryServer {
async fn register(
&self,
procedure: writer_schema::Procedure,
) -> Result<registered::Procedure, RegistrationError> {
let (transpiled_procedure, content_to_register) = transpile(procedure)?;
let mut registry = self.registry.lock().await;
for (resource_id, content) in content_to_register {
registry.insert(resource_id, content);
}
Ok(transpiled_procedure)
}
}
6 changes: 4 additions & 2 deletions pylib/traopy/src/models/runtime_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,23 @@ use pyo3_stub_gen::derive::*;
#[derive(Debug, Clone)]
pub struct PyRuntimeText {
pub name: String,
pub label: String,
}

#[gen_stub_pymethods]
#[pymethods]
impl PyRuntimeText {
#[new]
pub fn new(name: String) -> Self {
PyRuntimeText { name }
pub fn new(name: String, label: String) -> Self {
PyRuntimeText { name, label }
}
}

impl From<PyRuntimeText> for RuntimeText {
fn from(py_runtime_text: PyRuntimeText) -> Self {
RuntimeText {
name: py_runtime_text.name,
label: py_runtime_text.label,
}
}
}
18 changes: 15 additions & 3 deletions pylib/traopy/src/procedure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,21 @@ pub struct PyProcedure {
impl From<PyProcedure> for Procedure {
fn from(py_procedure: PyProcedure) -> Self {
Self {
resources: py_procedure.resources.clone(),
executions: py_procedure.executions.clone(),
scripts: py_procedure.scripts.clone(),
resources: py_procedure
.resources
.iter()
.map(|(_, v)| v.clone())
.collect(),
executions: py_procedure
.executions
.iter()
.map(|(_, v)| v.clone())
.collect(),
scripts: py_procedure
.scripts
.iter()
.map(|(_, v)| v.clone())
.collect(),
}
}
}
Expand Down

0 comments on commit 0481e5f

Please sign in to comment.