use super::*;
use mkentity::{Entity, ProjectSource};
use serde::{Deserialize, Serialize};
const USERNAME_UNIX_ENV_VAR: &str = "USER";
const USERNAME_WINDOWS_ENV_VAR: &str = "USERNAME";
const UNDEFINED_USERNAME: &str = "undefined_user";
#[derive(Default, Serialize, Deserialize)]
pub struct Staff {
#[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
id: Option<bson::oid::ObjectId>,
#[serde(rename = "staff_name")]
name: String,
#[serde(rename = "role")]
pub role_str: String,
#[serde(skip)]
pub(crate) role: ProductionRole,
#[serde(skip)]
#[cfg(all(feature = "ldap", feature = "image_processing"))]
pub(super) profile_img: Option<AnyResult<RetainedImage>>,
}
impl Staff {
pub fn from_env() -> Self {
let username = match std::env::var({
if cfg!(target_os = "windows") {
USERNAME_WINDOWS_ENV_VAR
} else {
USERNAME_UNIX_ENV_VAR
}
}) {
Ok(val) => val,
Err(_) => UNDEFINED_USERNAME.to_owned(),
};
Self::unknown_role(&username)
}
pub fn role(mut self, role: &ProductionRole) -> Self {
self.role_str = role.as_str();
self.role = role.clone();
self
}
pub fn role_from_str(mut self) -> Self {
self.role = self.role_str.as_str().into();
self
}
pub fn undefined() -> Self {
Self {
name: UNDEFINED_USERNAME.to_owned(),
..Default::default()
}
}
pub fn unknown_role(name: &str) -> Self {
Self {
name: name.to_owned(),
..Self::empty()
}
}
pub fn into_unknown_role(name: String) -> Self {
Self {
name,
..Self::empty()
}
}
pub fn artist_with_name(name: String) -> Self {
Self {
name,
role: ProductionRole::Artist,
role_str: ProductionRole::Artist.as_str(),
..Self::empty()
}
}
pub fn name_owned(self) -> String {
self.name
}
pub fn name_unwrap(&self) -> &String {
&self.name
}
pub fn name_mut(&mut self, name: &str) {
self.name = name.to_owned();
}
}
#[cfg(feature = "gui")]
impl Staff {
pub fn preview_name(&self, ui: &mut egui::Ui) {
ui.label(&self.name).on_hover_text(self.role.as_ref());
}
pub fn show_name_and_role(&self, ui: &mut egui::Ui) -> egui::Response {
ui.label(self.role.as_ref());
ui.colored_label(Color32::GREEN, format!("{}:", self.name_unwrap()))
}
#[cfg(all(feature = "ldap", feature = "image_processing"))]
pub fn show_profile_img(&self, ui: &mut egui::Ui, width_clamp: f32) {
let img = match &self.profile_img {
Some(Ok(img)) => img,
_ => IconCel::icon_book().get_included(embedded_icons::DUMMY_PROFILE),
};
let size = img.size_vec2();
let _response = ui.add(egui::Image::new(
img.texture_id(ui.ctx()),
[width_clamp, (size.y / size.x) * width_clamp],
));
#[cfg(debug_assertions)]
if let Some(Err(e)) = &self.profile_img {
_response.on_hover_text(e.to_string());
};
}
}
impl BsonId for Staff {
fn bson_id_as_ref(&self) -> Option<&ObjectId> {
self.id.as_ref()
}
fn bson_id(&self) -> AnyResult<&ObjectId> {
self.bson_id_as_ref().context("Staff without BSON ObjectId")
}
}
impl Entity for Staff {
fn empty() -> Self {
Self {
role_str: ProductionRole::default().as_str(),
..Default::default()
}
}
fn as_group(_group_name: &str, _typ: &ProjectSource) -> Self {
Self::empty()
}
fn name(&self) -> Option<&String> {
Some(&self.name)
}
}
impl fmt::Debug for Staff {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug = f.debug_struct("Staff");
debug
.field("id", &self.id)
.field("name", &self.name)
.field("role_str", &self.role_str)
.field("role", &self.role);
#[cfg(all(feature = "ldap", feature = "image_processing"))]
{
debug.field("has profile image", &self.profile_img.is_some());
}
debug.finish()
}
}
impl std::clone::Clone for Staff {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
name: self.name.clone(),
role_str: self.role_str.clone(),
role: self.role.clone(),
#[cfg(all(feature = "ldap", feature = "image_processing"))]
profile_img: None,
}
}
}
impl PartialEq for Staff {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl std::cmp::Eq for Staff {}
impl std::hash::Hash for Staff {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl PartialOrd for Staff {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.name.partial_cmp(&other.name)
}
}
impl Ord for Staff {
fn cmp(&self, other: &Self) -> Ordering {
self.name.cmp(&other.name)
}
}
impl From<Vec<Staff>> for RoleMap {
fn from(staves: Vec<Staff>) -> Self {
let mut layout = HashMap::<ProductionRole, HashSet<Staff>>::new();
for s in staves.into_iter() {
match layout.get_mut(&s.role) {
Some(group) => {
group.insert(s);
}
None => {
layout.insert(s.role.clone(), HashSet::from([s]));
}
}
}
RoleMap(layout)
}
}
#[derive(
Debug,
Clone,
Default,
Serialize,
Deserialize,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
strum::AsRefStr,
strum::EnumIter,
)]
pub enum ProductionRole {
#[strum(serialize = "Art Director")]
ArtDirector,
#[strum(serialize = "Art Producer")]
ArtProducer,
#[strum(serialize = "Team Lead")]
TeamLead,
Artist,
#[strum(serialize = "Tech Support")]
TechSupport,
Watcher,
Unassigned,
#[default]
Undefined,
}
impl ProductionRole {
pub fn is_supervisor(&self) -> bool {
matches!(
&self,
Self::ArtDirector | Self::ArtProducer | Self::TeamLead | Self::TechSupport
)
}
pub fn as_str(&self) -> String {
let role = match self {
Self::ArtDirector => "_AD",
Self::ArtProducer => "_PRODUCER",
Self::TeamLead => "_LEADER",
Self::Artist => "_ARTIST",
Self::TechSupport => "_TECH_SUPPORT",
Self::Watcher => "_WATCHER",
Self::Unassigned => "_UNASSIGNED",
Self::Undefined => "_UNDEFINED",
};
role.to_string()
}
#[cfg(feature = "query_message")]
pub fn qms_cron_fetch_seconds(&self) -> String {
let seconds = match self {
ProductionRole::ArtDirector => "1/10",
ProductionRole::ArtProducer | ProductionRole::TeamLead => "1/20",
ProductionRole::TechSupport => "1/7",
ProductionRole::Artist => "1/60",
_ => "1/120",
};
seconds.to_string()
}
#[cfg(feature = "alert")]
pub fn notif_cron_fetch_seconds(&self) -> u64 {
#[cfg(debug_assertions)]
return 30;
#[cfg(not(debug_assertions))]
150
}
}
impl From<&str> for ProductionRole {
fn from(role: &str) -> Self {
match role {
"_AD" => ProductionRole::ArtDirector,
"_PRODUCER" => ProductionRole::ArtProducer,
"_LEADER" => ProductionRole::TeamLead,
"_ARTIST" => ProductionRole::Artist,
"_TECH_SUPPORT" => ProductionRole::TechSupport,
"_WATCHER" => ProductionRole::Watcher,
_ => ProductionRole::Undefined,
}
}
}
#[cfg(feature = "gui")]
pub fn production_role_options_ui(ui: &mut egui::Ui, role: &mut ProductionRole) {
egui::ComboBox::from_label("Group")
.selected_text(role.as_ref())
.show_ui(ui, |ui| {
for r in ProductionRole::iter() {
if ui.selectable_label(role == &r, r.as_ref()).clicked() {
*role = r;
};
}
});
}