mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-02 16:38:14 +00:00
Fix compile issues
This commit is contained in:
1 parent
aa202172a2
commit
ea84b009a8
4 files changed
+180
-133
No files matched your search
@@ -30,7 +30,7 @@ pub enum InferenceResultType {
|
||||
#[serde(rename = "zs_voice_embedding")]
|
||||
ZeroShotVoiceEmbedding,
|
||||
|
||||
#[serde(renane = "upload_model")]
|
||||
#[serde(rename = "upload_model")]
|
||||
UploadModel
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ impl InferenceResultType {
|
||||
Self::TextToSpeech => "text_to_speech",
|
||||
Self::VoiceConversion => "voice_conversion",
|
||||
Self::ZeroShotVoiceEmbedding => "zs_voice_embedding",
|
||||
Self::UploadModel => "upload_model",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +56,7 @@ impl InferenceResultType {
|
||||
"text_to_speech" => Ok(Self::TextToSpeech),
|
||||
"voice_conversion" => Ok(Self::VoiceConversion),
|
||||
"zs_voice_embedding" => Ok(Self::ZeroShotVoiceEmbedding),
|
||||
"upload_model" => Ok(Self::UploadModel),
|
||||
_ => Err(format!("invalid value: {:?}", value)),
|
||||
}
|
||||
}
|
||||
@@ -67,6 +69,7 @@ impl InferenceResultType {
|
||||
InferenceResultType::TextToSpeech,
|
||||
InferenceResultType::VoiceConversion,
|
||||
InferenceResultType::ZeroShotVoiceEmbedding,
|
||||
InferenceResultType::UploadModel,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -82,6 +85,7 @@ mod tests {
|
||||
assert_serialization(InferenceResultType::TextToSpeech, "text_to_speech");
|
||||
assert_serialization(InferenceResultType::VoiceConversion, "voice_conversion");
|
||||
assert_serialization(InferenceResultType::ZeroShotVoiceEmbedding, "zs_voice_embedding");
|
||||
assert_serialization(InferenceResultType::UploadModel, "upload_model");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -90,6 +94,7 @@ mod tests {
|
||||
assert_eq!(InferenceResultType::TextToSpeech.to_str(), "text_to_speech");
|
||||
assert_eq!(InferenceResultType::VoiceConversion.to_str(), "voice_conversion");
|
||||
assert_eq!(InferenceResultType::ZeroShotVoiceEmbedding.to_str(), "zs_voice_embedding");
|
||||
assert_eq!(InferenceResultType::UploadModel.to_str(), "upload_model");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -98,6 +103,7 @@ mod tests {
|
||||
assert_eq!(InferenceResultType::from_str("text_to_speech").unwrap(), InferenceResultType::TextToSpeech);
|
||||
assert_eq!(InferenceResultType::from_str("voice_conversion").unwrap(), InferenceResultType::VoiceConversion);
|
||||
assert_eq!(InferenceResultType::from_str("zs_voice_embedding").unwrap(), InferenceResultType::ZeroShotVoiceEmbedding);
|
||||
assert_eq!(InferenceResultType::from_str("upload_model").unwrap(), InferenceResultType::UploadModel);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -109,6 +115,7 @@ mod tests {
|
||||
assert_eq!(variants.pop_first(), Some(InferenceResultType::TextToSpeech));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceResultType::VoiceConversion));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceResultType::ZeroShotVoiceEmbedding));
|
||||
assert_eq!(variants.pop_first(), Some(InferenceResultType::UploadModel));
|
||||
assert_eq!(variants.pop_first(), None);
|
||||
|
||||
// Generated check
|
||||
|
||||
@@ -551,15 +551,14 @@ Noosphere by skumerz + dalcefoPainting + 饭特稀V08 by zhazhahui345 + GhostMix
|
||||
let sd_15_weights_descriptor = Box::new(WeightsSD15Descriptor {});
|
||||
let lora_descriptor = Box::new(WeightsLoRADescriptor{});
|
||||
|
||||
|
||||
let path_object_SD = get_seed_tool_data_root().await?;
|
||||
let mut path_object_SD = get_seed_tool_data_root();
|
||||
path_object_SD.push("models/imagegen/sd15/majicmixFantasy_v30Vae.safetensors");
|
||||
|
||||
let path_object_loRA = get_seed_tool_data_root().await?;
|
||||
path_object_loRA.push("model/imagegen/loRA/xiawolei-v100-000019.safetensors");
|
||||
let mut path_object_loRA = get_seed_tool_data_root();
|
||||
path_object_loRA.push("models/imagegen/loRA/xiawolei-v100-000019.safetensors");
|
||||
|
||||
let metadata1 = remote_cloud_file_client.upload_file(sd_15_weights_descriptor,path_object_SD.string()).await?;
|
||||
let metadata2 = remote_cloud_file_client.upload_file(lora_descriptor,path_object_loRA.string()).await?;
|
||||
let metadata1 = remote_cloud_file_client.upload_file(sd_15_weights_descriptor,path_object_SD.as_path().to_str().unwrap()).await?;
|
||||
let metadata2 = remote_cloud_file_client.upload_file(lora_descriptor,path_object_loRA.as_path().to_str().unwrap()).await?;
|
||||
|
||||
let weights1 = CreateModelWeightsArgs {
|
||||
token: &model_weight_token1, // replace with actual ModelWeightToken
|
||||
@@ -576,9 +575,9 @@ Noosphere by skumerz + dalcefoPainting + 饭特稀V08 by zhazhahui345 + GhostMix
|
||||
original_filename: Some("majicmixFantasy_v30Vae.safetensors".to_string()),
|
||||
file_size_bytes: metadata1.file_size_bytes as i32,
|
||||
file_checksum_sha2: metadata1.sha256_checksum.to_string(),
|
||||
public_bucket_hash: metadata1.bucket_details.unwrap().object_hash,
|
||||
maybe_public_bucket_prefix: Some(metadata1.bucket_details.unwrap().prefix),
|
||||
maybe_public_bucket_extension: Some(metadata1.bucket_details.unwrap().suffix),
|
||||
public_bucket_hash: metadata1.bucket_details.clone().unwrap().object_hash,
|
||||
maybe_public_bucket_prefix: Some(metadata1.bucket_details.clone().unwrap().prefix),
|
||||
maybe_public_bucket_extension: Some(metadata1.bucket_details.clone().unwrap().suffix),
|
||||
cached_user_ratings_total_count: 10,
|
||||
cached_user_ratings_positive_count: 9,
|
||||
cached_user_ratings_negative_count: 1,
|
||||
@@ -602,9 +601,9 @@ Noosphere by skumerz + dalcefoPainting + 饭特稀V08 by zhazhahui345 + GhostMix
|
||||
original_filename: Some("xiawolei-v100-000019.safetensors".to_string()),
|
||||
file_size_bytes: metadata2.file_size_bytes as i32,
|
||||
file_checksum_sha2: metadata2.sha256_checksum.to_string(),
|
||||
public_bucket_hash: metadata2.bucket_details.unwrap().object_hash,
|
||||
maybe_public_bucket_prefix: Some(metadata2.bucket_details.unwrap().prefix),
|
||||
maybe_public_bucket_extension: Some(metadata2.bucket_details.unwrap().suffix),
|
||||
public_bucket_hash: metadata2.bucket_details.clone().unwrap().object_hash.clone(),
|
||||
maybe_public_bucket_prefix: Some(metadata2.bucket_details.clone().unwrap().prefix),
|
||||
maybe_public_bucket_extension: Some(metadata2.bucket_details.clone().unwrap().suffix),
|
||||
cached_user_ratings_total_count: 20,
|
||||
cached_user_ratings_positive_count: 9,
|
||||
cached_user_ratings_negative_count: 2,
|
||||
@@ -614,7 +613,6 @@ Noosphere by skumerz + dalcefoPainting + 饭特稀V08 by zhazhahui345 + GhostMix
|
||||
};
|
||||
|
||||
create_weight(weights1).await?;
|
||||
|
||||
create_weight(weights2).await?;
|
||||
|
||||
|
||||
@@ -933,7 +931,7 @@ pub async fn seed_weights(mysql_pool: &Pool<MySql>) -> AnyhowResult<()> {
|
||||
|
||||
//original_seed_weights(mysql_pool,user_token).await?;
|
||||
//seed_weights_for_user_token(mysql_pool, user_token).await?;
|
||||
seed_weights_for_paging(mysql_pool,user_token).await?;
|
||||
|
||||
//seed_weights_for_paging(mysql_pool,user_token).await?;
|
||||
seed_weights_for_testing_inference(mysql_pool,user_token).await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -226,3 +226,5 @@ async fn seed_file_to_bucket(
|
||||
maybe_bucket_extension: maybe_bucket_extension.map(|s| s.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+157
-117
@@ -1,10 +1,9 @@
|
||||
|
||||
use std::time::Duration;
|
||||
use cloud_storage::remote_file_manager::remote_cloud_bucket_details::RemoteCloudBucketDetails;
|
||||
use mysql_queries::payloads::generic_inference_args::image_generation_payload::StableDiffusionArgs;
|
||||
use mysql_queries::queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob;
|
||||
use mysql_queries::queries::model_weights::get_weight::get_weight_by_token;
|
||||
use crate::job::job_loop::job_success_result::{JobSuccessResult, ResultEntity};
|
||||
use crate::job::job_loop::job_success_result::{ JobSuccessResult, ResultEntity };
|
||||
use crate::job::job_loop::process_single_job_error::ProcessSingleJobError;
|
||||
use crate::job_dependencies::JobDependencies;
|
||||
use anyhow::anyhow;
|
||||
@@ -23,147 +22,142 @@ pub struct StableDiffusionProcessArgs<'a> {
|
||||
// upload model checkpoint or loRA
|
||||
|
||||
// create record in db
|
||||
// if stable_diffusion_args.inference_type == "checkpoint" {
|
||||
// // run inference with checkpoint and upload
|
||||
// } else if stable_diffusion_args.inference_type == "lora" {
|
||||
// // run inference with the lora + random checkpoint
|
||||
// } else if stable_diffusion_args.inference_type == "inference" {
|
||||
// // run inference with or without lora
|
||||
// } else {
|
||||
// return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("wrong inference type for job!")));
|
||||
// }
|
||||
// run inference
|
||||
// insert record into the db with the inference job token complete.
|
||||
// if stable_diffusion_args.inference_type == "checkpoint" {
|
||||
// // run inference with checkpoint and upload
|
||||
// } else if stable_diffusion_args.inference_type == "lora" {
|
||||
// // run inference with the lora + random checkpoint
|
||||
// } else if stable_diffusion_args.inference_type == "inference" {
|
||||
// // run inference with or without lora
|
||||
// } else {
|
||||
// return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("wrong inference type for job!")));
|
||||
// }
|
||||
// run inference
|
||||
// insert record into the db with the inference job token complete.
|
||||
|
||||
pub async fn download_from_google_link() {}
|
||||
|
||||
pub async fn download_from_google_link() {
|
||||
|
||||
}
|
||||
pub async fn sd_args_from_job(args: &StableDiffusionProcessArgs<'_>) -> Result<StableDiffusionArgs, ProcessSingleJobError> {
|
||||
pub async fn sd_args_from_job(
|
||||
args: &StableDiffusionProcessArgs<'_>
|
||||
) -> Result<StableDiffusionArgs, ProcessSingleJobError> {
|
||||
let inference_args = args.job.maybe_inference_args
|
||||
.as_ref()
|
||||
.map(|args| args.args.as_ref())
|
||||
.flatten();
|
||||
.as_ref()
|
||||
.map(|args| args.args.as_ref())
|
||||
.flatten();
|
||||
let polymorphic_args = match inference_args {
|
||||
Some(args) => args,
|
||||
None => {
|
||||
return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("no inference args for job!")));
|
||||
return Err(
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("no inference args for job!"))
|
||||
);
|
||||
}
|
||||
};
|
||||
let sd_args = match polymorphic_args {
|
||||
PolymorphicInferenceArgs::Ig(args) => args,
|
||||
_ => {
|
||||
return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("wrong inner args for job!")));
|
||||
return Err(
|
||||
ProcessSingleJobError::from_anyhow_error(anyhow!("wrong inner args for job!"))
|
||||
);
|
||||
}
|
||||
};
|
||||
let stable_diffusion_args: StableDiffusionArgs = StableDiffusionArgs::from(sd_args.clone());
|
||||
Ok(stable_diffusion_args)
|
||||
}
|
||||
|
||||
pub async fn process_job(args: StableDiffusionProcessArgs<'_>) -> Result<JobSuccessResult, ProcessSingleJobError> {
|
||||
pub async fn process_job(
|
||||
args: StableDiffusionProcessArgs<'_>
|
||||
) -> Result<JobSuccessResult, ProcessSingleJobError> {
|
||||
let job = args.job;
|
||||
let deps = args.job_dependencies;
|
||||
|
||||
// let sd_args = sd_args_from_job(&args).await?;
|
||||
|
||||
let sd_args = sd_args_from_job(&args).await?;
|
||||
|
||||
let mut job_progress_reporter = args.job_dependencies
|
||||
.clients
|
||||
.job_progress_reporter
|
||||
.new_generic_inference(job.inference_job_token.as_str())
|
||||
.map_err(|e| ProcessSingleJobError::Other(anyhow!(e)))?;
|
||||
//==================== TEMP DIR ==================== //
|
||||
let work_temp_dir = format!("temp_stable_diffusion_inference_{}", job.id.0);
|
||||
// let mut job_progress_reporter = args.job_dependencies.clients.job_progress_reporter
|
||||
// .new_generic_inference(job.inference_job_token.as_str())
|
||||
// .map_err(|e| ProcessSingleJobError::Other(anyhow!(e)))?;
|
||||
// //==================== TEMP DIR ==================== //
|
||||
// let work_temp_dir = format!("temp_stable_diffusion_inference_{}", job.id.0);
|
||||
|
||||
// //NB: TempDir exists until it goes out of scope, at which point it should delete from filesystem.
|
||||
// let work_temp_dir = args.job_dependencies.fs.scoped_temp_dir_creator_for_work
|
||||
// .new_tempdir(&work_temp_dir)
|
||||
// .map_err(|e| ProcessSingleJobError::from_io_error(e))?;
|
||||
|
||||
//NB: TempDir exists until it goes out of scope, at which point it should delete from filesystem.
|
||||
let work_temp_dir = args.job_dependencies
|
||||
.fs
|
||||
.scoped_temp_dir_creator_for_work
|
||||
.new_tempdir(&work_temp_dir)
|
||||
.map_err(|e| ProcessSingleJobError::from_io_error(e))?;
|
||||
// let sd_checkpoint_path = work_temp_dir.path().join("sd_checkpoint");
|
||||
// let lora_path = work_temp_dir.path().join("lora");
|
||||
|
||||
let sd_checkpoint_path = work_temp_dir.path().join("sd_checkpoint").to_str();
|
||||
let sd_checkpoint_path = match sd_checkpoint_path {
|
||||
Some(value) => {
|
||||
value
|
||||
},
|
||||
None => {
|
||||
// // Unpack loRA and Checkpoint
|
||||
// // run inference by downloading from google drive.
|
||||
// let lora_token = sd_args.maybe_lora_model_token;
|
||||
// let weight_token = sd_args.maybe_sd_model_token;
|
||||
|
||||
}
|
||||
};
|
||||
// let retrieved_sd_record = match weight_token {
|
||||
// Some(token) => {
|
||||
// let retrieved_sd_record = get_weight_by_token(
|
||||
// &token,
|
||||
// false,
|
||||
// &deps.db.mysql_pool
|
||||
// ).await?;
|
||||
// match retrieved_sd_record {
|
||||
// Some(record) => record,
|
||||
// None => {
|
||||
// return Err(
|
||||
// ProcessSingleJobError::from_anyhow_error(anyhow!("no record of model!"))
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// None => {
|
||||
// return Err(
|
||||
// ProcessSingleJobError::from_anyhow_error(anyhow!("no sd model token for job!"))
|
||||
// );
|
||||
// }
|
||||
// };
|
||||
|
||||
let lora_path = work_temp_dir.path().join("lora").to_str();
|
||||
let lora_path = match lora_path {
|
||||
Some(value) => {
|
||||
value
|
||||
},
|
||||
None => {
|
||||
}
|
||||
};
|
||||
// // ignore if no lora token
|
||||
// let retrieved_loRA_record = match lora_token {
|
||||
// Some(token) => {
|
||||
// let retrieved_loRA_record = get_weight_by_token(
|
||||
// &token,
|
||||
// false,
|
||||
// &deps.db.mysql_pool
|
||||
// ).await?;
|
||||
// Some(retrieved_loRA_record)
|
||||
// }
|
||||
// None => None,
|
||||
// };
|
||||
|
||||
// Unpack loRA and Checkpoint
|
||||
// run inference by downloading from google drive.
|
||||
let lora_token = sd_args.maybe_lora_model_token;
|
||||
let weight_token = sd_args.maybe_sd_model_token;
|
||||
// let details = RemoteCloudBucketDetails::new(
|
||||
// retrieved_sd_record.public_bucket_hash.clone(),
|
||||
// retrieved_sd_record.maybe_public_bucket_prefix.clone().unwrap_or_else(|| "".to_string()),
|
||||
// retrieved_sd_record.maybe_public_bucket_extension.clone().unwrap_or_else(|| "".to_string())
|
||||
// );
|
||||
|
||||
// let remote_cloud_file_client = RemoteCloudFileClient::get_remote_cloud_file_client().await?;
|
||||
// remote_cloud_file_client.download_file(details, sd_checkpoint_path.to_string()).await?;
|
||||
|
||||
let retrieved_sd_record = match weight_token {
|
||||
Some(token) => {
|
||||
let retrieved_sd_record = get_weight_by_token(
|
||||
&token,
|
||||
false,
|
||||
&deps.db.mysql_pool
|
||||
).await?;
|
||||
match retrieved_sd_record {
|
||||
Some(record) => record,
|
||||
None => {
|
||||
return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("no record of model!")))
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(ProcessSingleJobError::from_anyhow_error(anyhow!("no sd model token for job!")))
|
||||
}
|
||||
};
|
||||
// ignore if no lora token
|
||||
let retrieved_loRA_record = match lora_token {
|
||||
Some(token) => {
|
||||
let retrieved_loRA_record = get_weight_by_token(
|
||||
&token,
|
||||
false,
|
||||
&deps.db.mysql_pool
|
||||
).await?;
|
||||
Some(retrieved_loRA_record)
|
||||
},
|
||||
None => None
|
||||
};
|
||||
|
||||
let details = RemoteCloudBucketDetails::new(
|
||||
retrieved_sd_record.public_bucket_hash.clone(),
|
||||
retrieved_sd_record.maybe_public_bucket_prefix.clone().unwrap_or_else(|| "".to_string()),
|
||||
retrieved_sd_record.maybe_public_bucket_extension.clone().unwrap_or_else(|| "".to_string())
|
||||
);
|
||||
|
||||
let remote_cloud_file_client = RemoteCloudFileClient::get_remote_cloud_file_client().await?;
|
||||
remote_cloud_file_client.download_file(details, sd_checkpoint_path.to_string()).await?;
|
||||
|
||||
match retrieved_loRA_record {
|
||||
Some(record) => {
|
||||
match record {
|
||||
Some(model_weight_record) => {
|
||||
let lora_details = RemoteCloudBucketDetails::new(
|
||||
model_weight_record.public_bucket_hash.clone(),
|
||||
model_weight_record.maybe_public_bucket_prefix.clone().unwrap_or_else(|| "".to_string()),
|
||||
model_weight_record.maybe_public_bucket_extension.clone().unwrap_or_else(|| "".to_string())
|
||||
);
|
||||
remote_cloud_file_client.download_file(lora_details, lora_path.to_string()).await?;
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
// match retrieved_loRA_record {
|
||||
// Some(record) => {
|
||||
// match record {
|
||||
// Some(model_weight_record) => {
|
||||
// let lora_details = RemoteCloudBucketDetails::new(
|
||||
// model_weight_record.public_bucket_hash.clone(),
|
||||
// model_weight_record.maybe_public_bucket_prefix
|
||||
// .clone()
|
||||
// .unwrap_or_else(|| "".to_string()),
|
||||
// model_weight_record.maybe_public_bucket_extension
|
||||
// .clone()
|
||||
// .unwrap_or_else(|| "".to_string())
|
||||
// );
|
||||
// remote_cloud_file_client.download_file(
|
||||
// lora_details,
|
||||
// lora_path.to_str
|
||||
// ).await?;
|
||||
// }
|
||||
// None => {}
|
||||
// }
|
||||
// }
|
||||
// None => {}
|
||||
// }
|
||||
|
||||
// insert model record in create model record
|
||||
|
||||
@@ -173,7 +167,53 @@ pub async fn process_job(args: StableDiffusionProcessArgs<'_>) -> Result<JobSucc
|
||||
// };
|
||||
|
||||
Ok(JobSuccessResult {
|
||||
maybe_result_entity: None,
|
||||
inference_duration: Duration::from_secs(0),
|
||||
maybe_result_entity: None,
|
||||
inference_duration: Duration::from_secs(0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::anyhow;
|
||||
use std::path::PathBuf;
|
||||
use cloud_storage::remote_file_manager::{
|
||||
remote_cloud_file_manager::{RemoteCloudFileClient, self},
|
||||
remote_cloud_bucket_details::RemoteCloudBucketDetails,
|
||||
};
|
||||
use errors::{AnyhowError, AnyhowResult};
|
||||
#[tokio::test]
|
||||
async fn test_seed_weights_files() -> AnyhowResult<()> {
|
||||
let seed_path = PathBuf::from("/storyteller/root/custom-seed-tool-data");
|
||||
let remote_cloud_file_client = RemoteCloudFileClient::get_remote_cloud_file_client().await;
|
||||
let remote_cloud_file_client = match remote_cloud_file_client {
|
||||
Ok(res) => {
|
||||
res
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(anyhow!("failed to get remote cloud file client"));
|
||||
}
|
||||
};
|
||||
|
||||
let mut path_dl1 = seed_path.clone();
|
||||
path_dl1.push("downloads/loRA");
|
||||
let mut path_dl2 = seed_path.clone();
|
||||
path_dl2.push("downloads/checkpoint");
|
||||
|
||||
let bucket_details1 = RemoteCloudBucketDetails {
|
||||
object_hash: String::from("apa0ej6es8d3ss2gwtf1cghge35qn9tn"),
|
||||
prefix: String::from("sd15"),
|
||||
suffix: String::from("safetensors"),
|
||||
};
|
||||
|
||||
let bucket_details2 = RemoteCloudBucketDetails {
|
||||
object_hash: String::from("27kz11et18fargyyxbj66ntfn621k9d3"),
|
||||
prefix: String::from("loRA"),
|
||||
suffix: String::from("safetensors"),
|
||||
};
|
||||
|
||||
remote_cloud_file_client.download_file(bucket_details1, String::from("./checkpoint")).await?;
|
||||
remote_cloud_file_client.download_file(bucket_details2, String::from("./loRA")).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user