tfcconnection/src/api/health_form.rs

417 lines
16 KiB
Rust

use std::{collections::HashMap, fs};
use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
use actix_web::{post, HttpResponse};
use color_eyre::{eyre::eyre, Result};
use lettre::{
message::{header::ContentType, Attachment, MultiPart, SinglePart},
Message,
};
use maud::{html, Markup, DOCTYPE};
use reqwest::Client;
use tracing::{error, info};
use crate::email::send_email;
#[derive(Debug, MultipartForm)]
struct HealthForm {
#[multipart(rename = "first-name")]
first_name: Text<String>,
#[multipart(rename = "last-name")]
last_name: Text<String>,
#[multipart(rename = "parent-first-name")]
parent_first_name: Text<String>,
#[multipart(rename = "parent-last-name")]
parent_last_name: Text<String>,
#[multipart(rename = "birth-date")]
birthdate: Text<String>,
street: Text<String>,
city: Text<String>,
state: Text<String>,
zip: Text<String>,
#[multipart(rename = "cell-phone")]
parent_cellphone: Text<String>,
#[multipart(rename = "home-phone")]
homephone: Text<String>,
#[multipart(rename = "additional-emergency-contact")]
contact: Text<String>,
#[multipart(rename = "additional-emergency-contact-phone")]
contact_phone: Text<String>,
#[multipart(rename = "doctor-name")]
doctorname: Text<String>,
#[multipart(rename = "doctor-city")]
doctorcity: Text<String>,
#[multipart(rename = "doctor-phone")]
doctorphone: Text<String>,
#[multipart(rename = "medical-coverage")]
medical: Text<String>,
#[multipart(rename = "insurance-name")]
insurance: Text<String>,
#[multipart(rename = "policy-number")]
policy_number: Text<String>,
allergies: Text<String>,
#[multipart(rename = "allergies-other")]
allergies_other: Text<String>,
#[multipart(rename = "specific-allergies")]
specific_allergies: Text<String>,
#[multipart(rename = "allergic-treatment")]
treatment: Text<String>,
conditions: Text<String>,
#[multipart(rename = "tetanus-shot")]
tetanus: Text<String>,
#[multipart(rename = "swimming-ability")]
swimming: Text<String>,
#[multipart(rename = "medication-schedule")]
medication: Text<String>,
#[multipart(rename = "other-notes")]
notes: Text<String>,
agreement: Text<String>,
#[multipart(rename = "image")]
file: Option<TempFile>,
registration: Text<String>,
}
impl From<&HealthForm> for HashMap<i32, String> {
fn from(form: &HealthForm) -> Self {
let mut map = HashMap::new();
map.insert(37, format!("{} {}", form.first_name.0, form.last_name.0));
map.insert(
38,
format!("{} {}", form.parent_first_name.0, form.parent_last_name.0),
);
map.insert(39, form.birthdate.0.clone());
map.insert(40, form.street.0.clone());
map.insert(41, form.city.0.clone());
map.insert(42, form.state.0.clone());
map.insert(43, form.zip.0.clone());
map.insert(44, form.parent_cellphone.0.clone());
map.insert(45, form.homephone.0.clone());
map.insert(46, format!("{} {}", form.contact.0, form.contact_phone.0));
map.insert(47, form.doctorname.0.clone());
map.insert(48, form.doctorcity.0.clone());
map.insert(49, form.doctorphone.0.clone());
map.insert(50, form.medical.0.clone());
map.insert(51, form.insurance.0.clone());
map.insert(52, form.policy_number.0.clone());
map.insert(54, form.agreement.0.clone());
map.insert(
55,
format!("{} \n {}", form.allergies.0, form.allergies_other.0),
);
map.insert(56, form.specific_allergies.0.clone());
map.insert(57, form.treatment.0.clone());
map.insert(58, form.conditions.0.clone());
map.insert(59, form.tetanus.0.clone());
map.insert(60, form.medication.0.clone());
map.insert(61, form.notes.0.clone());
map.insert(62, form.swimming.0.clone());
map
}
}
impl HealthForm {
async fn build_email(&self) -> Markup {
html! {
(DOCTYPE)
meta charset="utf-8";
html {
head {
title { (self.first_name.0) " " (self.last_name.0) " filled out a health form!" }
style {
"table { border-collapse: collapse; width: 100% }"
"td, th { padding: 8px }"
"td { text-align: left; width: 70%; word-wrap: break-word }"
"th { text-align: right; border-right: 1px solid #ddd }"
"tr { border-bottom: 1px solid #ddd }"
"h1 { text-align: center }"
}
}
body {
h1 { "Health form for " (self.first_name.0) " " (self.last_name.0) "!" }
hr;
table {
tr {
th { "Name" }
td { (self.first_name.0) " " (self.last_name.0) }
}
tr {
th { "Parent" }
td { (self.parent_first_name.0) " " (self.parent_last_name.0) }
}
tr {
th { "Birthdate" }
td { (self.birthdate.0) }
}
tr {
th { "Street" }
td { (self.street.0) }
}
tr {
th { "City" }
td { (self.city.0) }
}
tr {
th { "State" }
td { (self.state.0) }
}
tr {
th { "Zip" }
td { (self.zip.0) }
}
tr {
th { "Parent Cell Phone" }
td { (self.parent_cellphone.0) }
}
tr {
th { "Homephone" }
td { (self.homephone.0) }
}
tr {
th { "Additional Emergency Contact" }
td { (self.contact.0) }
}
tr {
th { "Emegency Contact Phone" }
td { (self.contact_phone.0) }
}
tr {
th { "Doctor" }
td { (self.doctorname.0) }
}
tr {
th { "Doctor City" }
td { (self.doctorcity.0) }
}
tr {
th { "Doctor Phone" }
td { (self.doctorphone.0) }
}
tr {
th { "Medical Coverage" }
td { (self.medical.0) }
}
tr {
th { "Insurance Provider" }
td { (self.insurance.0) }
}
tr {
th { "Policy Number" }
td { (self.policy_number.0) }
}
tr {
th { "Allergies" }
td { (self.allergies.0)
"\n\n"
(self.allergies_other.0)
}
}
tr {
th { "Specific Allergies" }
td { (self.specific_allergies.0) }
}
tr {
th { "Allergic Treatments" }
td { (self.treatment.0) }
}
tr {
th { "Conditions" }
td { (self.conditions.0) }
}
tr {
th { "Date of last Tetanus Shot" }
td { (self.tetanus.0) }
}
tr {
th { "Swimming Ability" }
td { (self.swimming.0) }
}
tr {
th { "Medication Schedule" }
td { (self.medication.0) }
}
tr {
th { "Other Notes" }
td { (self.notes.0) }
}
tr {
th { "Final Agreement" }
td { (self.agreement.0) }
}
tr {
th { "Registration" }
td { (self.registration.0) }
}
}
}
}
}
}
async fn store_form(&self) -> Result<()> {
let client = Client::new();
let map = HashMap::from(self);
let mut json = HashMap::new();
json.insert("data", map);
let link = r#"https://staff.tfcconnection.org/apps/tables/#/table/4/row/757"#;
let res = client
.post("https://staff.tfcconnection.org/ocs/v2.php/apps/tables/api/2/tables/4/rows")
.basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b"))
.header("OCS-APIRequest", "true")
.header("Content-Type", "application/json")
.json(&json)
.send()
.await?;
if res.status().is_success() {
let res = res.text().await.unwrap();
Ok(())
} else {
Err(eyre!(
"Problem in storing data: {:?}",
res.error_for_status()
))
}
}
fn get_temp_file(&mut self) -> Option<(String, String)> {
let first = self.first_name.clone();
let last = self.last_name.clone();
let filename_noext = format!("{}_{}", first, last);
let file_name = if let Some(file) = self.file.as_ref() {
file.file_name.to_owned()
} else {
return None;
};
let filename;
let path = if let Some(file_name) = file_name {
if let Some(ext) = file_name.rsplit('.').next() {
filename = format!("{}.{}", filename_noext, ext);
format!("./tmp/{}.{}", filename_noext, ext)
} else {
filename = String::default();
format!("./tmp/{}", file_name)
}
} else {
filename = String::default();
String::default()
};
let file = self.file.take();
match file.unwrap().file.persist(path.clone()) {
Ok(f) => {
if f.metadata().unwrap().len() <= 0 {
return None;
}
info!(?f, "File saved successfully");
Some((filename, path))
}
Err(e) => {
error!("{:?}: Probably a missing image", e);
None
}
}
}
async fn send_email(&mut self) -> Result<()> {
let first = self.first_name.clone();
let last = self.last_name.clone();
let email_subject = format!("{} {} filled out a health form!", first, last);
info!("{first} {last} filled out a health form!");
let email = self.build_email().await;
let temp_file = self.get_temp_file();
let multi = if let Some((file, path)) = temp_file {
let filebody = fs::read(path);
let content_type = ContentType::parse("image/jpg").unwrap();
let attachment = Attachment::new(file).body(filebody.unwrap(), content_type);
// info!(?attachment);
MultiPart::mixed()
.singlepart(SinglePart::html(email.into_string()))
.singlepart(attachment)
} else {
MultiPart::alternative_plain_html(String::from("Testing"), email.into_string())
};
if let Ok(m) = Message::builder()
.from(
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
.parse()
.unwrap(),
)
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
.subject(email_subject)
.multipart(multi)
{
send_email(m).await
} else {
Err(eyre!("Email incorrect"))
}
}
}
#[post("/api/health-form")]
pub async fn health_form(MultipartForm(mut form): MultipartForm<HealthForm>) -> HttpResponse {
info!("Starting health form work: {:?}", form);
match form.send_email().await {
Ok(_) => info!("Successfully sent email health form"),
Err(e) => error!("There was an error sending email: {e}"),
}
let full_name = format!("{} {}", form.first_name.0, form.last_name.0);
match form.registration.0.as_str() {
"now" => {
info!("Sending them to pay for registration now");
HttpResponse::Ok()
.insert_header(("Access-Control-Expose-Headers", "*"))
.insert_header((
"HX-Redirect",
"https://secure.myvanco.com/L-Z772/campaign/C-13JPJ",
))
.finish()
}
"full" => {
info!("Sending them to pay for the full registration now");
HttpResponse::Ok()
.insert_header(("Access-Control-Expose-Headers", "*"))
.insert_header((
"HX-Redirect",
"https://secure.myvanco.com/L-Z772/campaign/C-13JQE",
))
.finish()
}
"later" => {
info!("{} would like to pay later", full_name);
let html = html! {
div class="mt-8" {
h2 {
"Thank you, " (full_name) "!"
}
p { "Can't wait to see you at camp!" }
p {
class { "" }
"If you'd like to pay for your registration go to the donate tab in the top right when you are ready and find the camp registration option."
}
}
};
HttpResponse::Ok().body(html.into_string())
}
_ => {
log::warn!(
"Got registration error possibly. Here is what the registration was: {}",
form.registration.0.as_str()
);
let html = html! {
div class="mt-8" {
h2 {
"Thank you, " (full_name) "!"
}
p {
class { "" }
"If you filled this out for camp or mission trip you can pay for your registration at the donate tab in the top right when you are ready and find the camp or mission trip registration option."
}
}
};
HttpResponse::Ok().body(html.into_string())
}
}
// HttpResponse::Ok().body("hi")
}