Entferne automatische Annahme alter Bewerbungen und implementiere Entscheidung durch NPCs für offene Stellen ohne Vorgesetzten
All checks were successful
Deploy yourpart (blue-green) / deploy (push) Successful in 5m13s
All checks were successful
Deploy yourpart (blue-green) / deploy (push) Successful in 5m13s
This commit is contained in:
@@ -39,10 +39,7 @@ use crate::worker::sql::{
|
||||
QUERY_GET_CHURCH_OFFICE_OCCUPIED_COUNT,
|
||||
QUERY_IS_CHARACTER_NPC,
|
||||
QUERY_GET_PENDING_CHURCH_APPLICATIONS_FOR_SCORING,
|
||||
QUERY_INTERIM_APPOINT_CHURCH_OFFICE,
|
||||
QUERY_UPDATE_CHARACTER_HIGHEST_CHURCH_FROM_OFFICE_TYPE,
|
||||
QUERY_FIND_INTERIM_CHURCH_NPC_CANDIDATE,
|
||||
QUERY_REMOVE_LOWER_CHURCH_OFFICES_FOR_CHARACTER,
|
||||
QUERY_GET_PENDING_CHURCH_APPLICATIONS_WITHOUT_SUPERVISOR,
|
||||
};
|
||||
|
||||
pub struct PoliticsWorker {
|
||||
@@ -81,7 +78,6 @@ struct Office {
|
||||
#[derive(Debug, Clone)]
|
||||
struct AvailableChurchOffice {
|
||||
office_type_id: i32,
|
||||
hierarchy_level: i32,
|
||||
seats_per_region: i32,
|
||||
region_id: i32,
|
||||
occupied_seats: i32,
|
||||
@@ -92,9 +88,6 @@ struct ChurchSupervisor {
|
||||
supervisor_character_id: i32,
|
||||
}
|
||||
|
||||
/// Bis einschließlich dieser `hierarchy_level` (church_office_type): Interimsbesetzung ohne Vorgesetzten.
|
||||
const INTERIM_MAX_CHURCH_HIERARCHY: i32 = 6;
|
||||
|
||||
struct ChurchAppScoreRow {
|
||||
application_id: i32,
|
||||
office_type_id: i32,
|
||||
@@ -116,7 +109,6 @@ impl PoliticsWorker {
|
||||
|
||||
fn run_loop(pool: ConnectionPool, broker: MessageBroker, state: Arc<WorkerState>) {
|
||||
let mut last_execution: Option<Instant> = None;
|
||||
let mut last_auto_approve_run: Option<Instant> = None;
|
||||
|
||||
while state.running_worker.load(Ordering::Relaxed) {
|
||||
let now = Instant::now();
|
||||
@@ -132,21 +124,6 @@ impl PoliticsWorker {
|
||||
last_execution = Some(now);
|
||||
}
|
||||
|
||||
// Automatische Annahme alter Applications (stündlich)
|
||||
let should_run_auto_approve = match last_auto_approve_run {
|
||||
None => true,
|
||||
Some(prev) => {
|
||||
now.saturating_duration_since(prev) >= Duration::from_secs(3600)
|
||||
}
|
||||
};
|
||||
|
||||
if should_run_auto_approve {
|
||||
if let Err(err) = Self::auto_approve_old_church_applications(&pool, &broker) {
|
||||
eprintln!("[PoliticsWorker] Fehler bei auto_approve_old_church_applications: {err}");
|
||||
}
|
||||
last_auto_approve_run = Some(now);
|
||||
}
|
||||
|
||||
// Entspricht ungefähr der 5-Sekunden-Schleife im C++-Code
|
||||
for _ in 0..5 {
|
||||
if !state.running_worker.load(Ordering::Relaxed) {
|
||||
@@ -770,18 +747,37 @@ impl PoliticsWorker {
|
||||
pool,
|
||||
office.office_type_id,
|
||||
office.region_id,
|
||||
supervisor.supervisor_character_id,
|
||||
Some(supervisor.supervisor_character_id),
|
||||
need,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
} else if office.hierarchy_level <= INTERIM_MAX_CHURCH_HIERARCHY {
|
||||
Self::try_interim_church_appointment(pool, broker, office)?;
|
||||
} else {
|
||||
eprintln!(
|
||||
"[PoliticsWorker] Kein Supervisor, Interim deaktiviert (hierarchy_level={}): office_type_id={}, region_id={}",
|
||||
office.hierarchy_level, office.office_type_id, office.region_id
|
||||
);
|
||||
// Einstiegsämter haben naturgemäß keinen Vorgesetzten. Offene
|
||||
// Spielerbewerbungen dürfen deshalb nicht dauerhaft pending
|
||||
// bleiben: Der Kirchen-NPC entscheidet sie für diese Stelle.
|
||||
Self::npc_resolve_church_applications_without_supervisor(pool, broker, office)?;
|
||||
|
||||
let occupied = Self::get_church_occupied_count(
|
||||
pool,
|
||||
office.office_type_id,
|
||||
office.region_id,
|
||||
)?;
|
||||
let pending = Self::count_pending_church_apps(
|
||||
pool,
|
||||
office.office_type_id,
|
||||
office.region_id,
|
||||
)?;
|
||||
let missing_applications = (office.seats_per_region - occupied - pending).max(0);
|
||||
if missing_applications > 0 {
|
||||
Self::create_church_application_jobs(
|
||||
pool,
|
||||
office.office_type_id,
|
||||
office.region_id,
|
||||
None,
|
||||
missing_applications,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -854,7 +850,9 @@ impl PoliticsWorker {
|
||||
age_days: i32,
|
||||
wait_days: f64,
|
||||
) -> f64 {
|
||||
let age_years = age_days / 365;
|
||||
// Ein Kalendertag ist ein Spieljahr; die Datenbankdifferenz ist somit
|
||||
// bereits das Alter in Spieljahren.
|
||||
let age_years = age_days;
|
||||
let age_bonus = if (25..=70).contains(&age_years) {
|
||||
12.0
|
||||
} else {
|
||||
@@ -950,100 +948,6 @@ impl PoliticsWorker {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn remove_lower_ranked_church_offices_for_character(
|
||||
pool: &ConnectionPool,
|
||||
character_id: i32,
|
||||
) -> Result<(), DbError> {
|
||||
let mut conn = pool
|
||||
.get()
|
||||
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
conn.prepare(
|
||||
"rm_lower_church",
|
||||
QUERY_REMOVE_LOWER_CHURCH_OFFICES_FOR_CHARACTER,
|
||||
)
|
||||
.map_err(|e| DbError::new(format!("[PoliticsWorker] prepare rm_lower_church: {e}")))?;
|
||||
conn.execute("rm_lower_church", &[&character_id])
|
||||
.map_err(|e| DbError::new(format!("[PoliticsWorker] exec rm_lower_church: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_interim_church_appointment(
|
||||
pool: &ConnectionPool,
|
||||
broker: &MessageBroker,
|
||||
office: &AvailableChurchOffice,
|
||||
) -> Result<(), DbError> {
|
||||
let mut conn = pool
|
||||
.get()
|
||||
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
conn.prepare(
|
||||
"find_interim_npc",
|
||||
QUERY_FIND_INTERIM_CHURCH_NPC_CANDIDATE,
|
||||
)
|
||||
.map_err(|e| DbError::new(format!("[PoliticsWorker] prepare find_interim_npc: {e}")))?;
|
||||
let rows = conn
|
||||
.execute("find_interim_npc", &[&office.region_id, &office.office_type_id])
|
||||
.map_err(|e| DbError::new(format!("[PoliticsWorker] exec find_interim_npc: {e}")))?;
|
||||
|
||||
let candidate_id = rows
|
||||
.first()
|
||||
.and_then(|r| r.get("character_id"))
|
||||
.and_then(|v| v.parse::<i32>().ok())
|
||||
.unwrap_or(-1);
|
||||
if candidate_id < 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !Self::character_eligible_for_church_office(pool, candidate_id, office.office_type_id)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
drop(conn);
|
||||
let mut conn = pool
|
||||
.get()
|
||||
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
conn.prepare(
|
||||
"interim_ins",
|
||||
QUERY_INTERIM_APPOINT_CHURCH_OFFICE,
|
||||
)
|
||||
.map_err(|e| DbError::new(format!("[PoliticsWorker] prepare interim_ins: {e}")))?;
|
||||
let ins = conn
|
||||
.execute(
|
||||
"interim_ins",
|
||||
&[
|
||||
&office.office_type_id,
|
||||
&candidate_id,
|
||||
&office.region_id,
|
||||
&office.seats_per_region,
|
||||
],
|
||||
)
|
||||
.map_err(|e| DbError::new(format!("[PoliticsWorker] exec interim_ins: {e}")))?;
|
||||
|
||||
if ins.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
conn.prepare(
|
||||
"upd_hi_interim",
|
||||
QUERY_UPDATE_CHARACTER_HIGHEST_CHURCH_FROM_OFFICE_TYPE,
|
||||
)
|
||||
.map_err(|e| DbError::new(format!("[PoliticsWorker] prepare upd_hi_interim: {e}")))?;
|
||||
conn.execute("upd_hi_interim", &[&candidate_id, &office.office_type_id])
|
||||
.map_err(|e| DbError::new(format!("[PoliticsWorker] exec upd_hi_interim: {e}")))?;
|
||||
|
||||
Self::remove_lower_ranked_church_offices_for_character(pool, candidate_id)?;
|
||||
|
||||
eprintln!(
|
||||
"[PoliticsWorker] Interims-Kirchenamt: character_id={}, office_type_id={}, region_id={}",
|
||||
candidate_id, office.office_type_id, office.region_id
|
||||
);
|
||||
|
||||
if let Some(uid) = Self::get_user_id_for_character(pool, candidate_id)? {
|
||||
Self::publish_falukant_church_update(broker, uid, "vacancy_fill");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_available_church_offices(
|
||||
pool: &ConnectionPool,
|
||||
) -> Result<Vec<AvailableChurchOffice>, DbError> {
|
||||
@@ -1071,7 +975,6 @@ impl PoliticsWorker {
|
||||
let mut offices = Vec::new();
|
||||
for row in rows {
|
||||
let office_type_id = parse_i32(&row, "office_type_id", -1);
|
||||
let hierarchy_level = parse_i32(&row, "hierarchy_level", 99);
|
||||
let region_id = parse_i32(&row, "region_id", -1);
|
||||
let seats_per_region = parse_i32(&row, "seats_per_region", 0);
|
||||
let occupied_seats = parse_i32(&row, "occupied_seats", 0);
|
||||
@@ -1079,7 +982,6 @@ impl PoliticsWorker {
|
||||
if office_type_id >= 0 && region_id >= 0 {
|
||||
offices.push(AvailableChurchOffice {
|
||||
office_type_id,
|
||||
hierarchy_level,
|
||||
seats_per_region,
|
||||
region_id,
|
||||
occupied_seats,
|
||||
@@ -1336,11 +1238,102 @@ impl PoliticsWorker {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Entscheidet Bewerbungen für Ämter, die noch keinen Vorgesetzten haben.
|
||||
/// Das betrifft insbesondere `lay-preacher`: Ohne diesen Pfad bleiben die
|
||||
/// vom Backend erzeugten Bewerbungen für immer auf `pending`.
|
||||
fn npc_resolve_church_applications_without_supervisor(
|
||||
pool: &ConnectionPool,
|
||||
broker: &MessageBroker,
|
||||
office: &AvailableChurchOffice,
|
||||
) -> Result<(), DbError> {
|
||||
let mut conn = pool
|
||||
.get()
|
||||
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
conn.prepare(
|
||||
"score_church_apps_no_supervisor",
|
||||
QUERY_GET_PENDING_CHURCH_APPLICATIONS_WITHOUT_SUPERVISOR,
|
||||
)?;
|
||||
conn.prepare("reject_church_application", QUERY_REJECT_CHURCH_APPLICATION)?;
|
||||
conn.prepare("approve_church_application", QUERY_APPROVE_CHURCH_APPLICATION)?;
|
||||
|
||||
let rows = conn.execute(
|
||||
"score_church_apps_no_supervisor",
|
||||
&[&office.office_type_id, &office.region_id],
|
||||
)?;
|
||||
if rows.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut candidates = Vec::new();
|
||||
for row in rows {
|
||||
let application_id = parse_i32(&row, "application_id", -1);
|
||||
let applicant_character_id = parse_i32(&row, "applicant_character_id", -1);
|
||||
if application_id < 0 || applicant_character_id < 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !Self::character_eligible_for_church_office(
|
||||
pool,
|
||||
applicant_character_id,
|
||||
office.office_type_id,
|
||||
)? {
|
||||
conn.execute("reject_church_application", &[&application_id])?;
|
||||
if let Some(uid) = Self::get_user_id_for_character(pool, applicant_character_id)? {
|
||||
Self::publish_falukant_church_update(broker, uid, "npc_decision");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let score = Self::church_candidate_score(
|
||||
row.get("supervisor_reputation")
|
||||
.and_then(|v| v.parse::<f64>().ok())
|
||||
.unwrap_or(50.0),
|
||||
row.get("applicant_reputation")
|
||||
.and_then(|v| v.parse::<f64>().ok())
|
||||
.unwrap_or(50.0),
|
||||
parse_i32(&row, "applicant_highest_ever", 0),
|
||||
parse_i32(&row, "applicant_current_max_hierarchy", 0),
|
||||
parse_i32(&row, "applicant_title_level", 0),
|
||||
parse_i32(&row, "applicant_age_days", 0),
|
||||
0.0,
|
||||
);
|
||||
candidates.push((application_id, applicant_character_id, score));
|
||||
}
|
||||
|
||||
candidates.sort_by(|a, b| {
|
||||
b.2.partial_cmp(&a.2)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
});
|
||||
|
||||
let mut occupied = Self::get_church_occupied_count(
|
||||
pool,
|
||||
office.office_type_id,
|
||||
office.region_id,
|
||||
)?;
|
||||
for (application_id, applicant_character_id, _) in candidates {
|
||||
let approved = occupied < office.seats_per_region
|
||||
&& !conn
|
||||
.execute("approve_church_application", &[&application_id])?
|
||||
.is_empty();
|
||||
if approved {
|
||||
occupied += 1;
|
||||
} else {
|
||||
conn.execute("reject_church_application", &[&application_id])?;
|
||||
}
|
||||
|
||||
if let Some(uid) = Self::get_user_id_for_character(pool, applicant_character_id)? {
|
||||
Self::publish_falukant_church_update(broker, uid, "npc_decision");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_church_application_jobs(
|
||||
pool: &ConnectionPool,
|
||||
office_type_id: i32,
|
||||
region_id: i32,
|
||||
supervisor_id: i32,
|
||||
supervisor_id: Option<i32>,
|
||||
count: i32,
|
||||
) -> Result<(), DbError> {
|
||||
let mut conn = pool
|
||||
@@ -1359,7 +1352,10 @@ impl PoliticsWorker {
|
||||
})?;
|
||||
|
||||
let rows = conn
|
||||
.execute("get_characters_for_church_office", &[®ion_id, &count])
|
||||
.execute(
|
||||
"get_characters_for_church_office",
|
||||
&[®ion_id, &office_type_id, &count],
|
||||
)
|
||||
.map_err(|e| {
|
||||
DbError::new(format!(
|
||||
"[PoliticsWorker] exec get_characters_for_church_office: {e}"
|
||||
@@ -1411,7 +1407,9 @@ impl PoliticsWorker {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Automatische Annahme von Church Applications, die älter als 36 Stunden sind
|
||||
/// Der frühere stündliche Auto-Approve ist bewusst nicht mehr im Scheduler
|
||||
/// registriert; Entscheidungen laufen nach der zweitägigen Bewerbungsfrist.
|
||||
#[allow(dead_code)]
|
||||
fn auto_approve_old_church_applications(
|
||||
pool: &ConnectionPool,
|
||||
broker: &MessageBroker,
|
||||
|
||||
@@ -3215,7 +3215,8 @@ pub const QUERY_CREATE_CHURCH_APPLICATION_JOB: &str = r#"
|
||||
RETURNING id;
|
||||
"#;
|
||||
|
||||
/// Nur NPCs: Spielerbewerbungen laufen über die UI.
|
||||
/// Nur NPCs: Für den Einstieg ohne Amt, für eine Beförderung ausschließlich aus
|
||||
/// dem unmittelbar niedrigeren derzeitigen Kirchenamt.
|
||||
pub const QUERY_GET_CHARACTERS_FOR_CHURCH_OFFICE: &str = r#"
|
||||
SELECT DISTINCT
|
||||
c.id AS character_id,
|
||||
@@ -3224,17 +3225,29 @@ pub const QUERY_GET_CHARACTERS_FOR_CHURCH_OFFICE: &str = r#"
|
||||
c.title_of_nobility,
|
||||
t.level AS title_level
|
||||
FROM falukant_data.character c
|
||||
JOIN falukant_type.church_office_type target ON target.id = $2::int
|
||||
LEFT JOIN falukant_type.title t ON c.title_of_nobility = t.id
|
||||
WHERE c.region_id = $1
|
||||
AND c.health > 0
|
||||
AND c.user_id IS NULL
|
||||
AND NOT EXISTS(
|
||||
SELECT 1
|
||||
FROM falukant_data.church_office co
|
||||
WHERE co.character_id = c.id
|
||||
AND (
|
||||
(target.hierarchy_level <= 1 AND NOT EXISTS(
|
||||
SELECT 1
|
||||
FROM falukant_data.church_office co
|
||||
WHERE co.character_id = c.id
|
||||
))
|
||||
OR
|
||||
(target.hierarchy_level > 1 AND EXISTS(
|
||||
SELECT 1
|
||||
FROM falukant_data.church_office co
|
||||
JOIN falukant_type.church_office_type current
|
||||
ON current.id = co.office_type_id
|
||||
WHERE co.character_id = c.id
|
||||
AND current.hierarchy_level = target.hierarchy_level - 1
|
||||
))
|
||||
)
|
||||
ORDER BY RANDOM()
|
||||
LIMIT $2;
|
||||
LIMIT $3;
|
||||
"#;
|
||||
|
||||
pub const QUERY_COUNT_PENDING_CHURCH_APPS_BY_OFFICE_REGION: &str = r#"
|
||||
@@ -3285,9 +3298,45 @@ pub const QUERY_GET_PENDING_CHURCH_APPLICATIONS_FOR_SCORING: &str = r#"
|
||||
LEFT JOIN falukant_type.title t ON t.id = ac.title_of_nobility
|
||||
WHERE ca.status = 'pending'
|
||||
AND ca.supervisor_id = $1::int
|
||||
AND ca.created_at <= NOW() - INTERVAL '2 days'
|
||||
ORDER BY ca.created_at ASC;
|
||||
"#;
|
||||
|
||||
/// Einstiegsämter haben noch keinen Amtsinhaber als Vorgesetzten. Diese offenen
|
||||
/// Bewerbungen werden deshalb vom Kirchen-NPC für die konkrete Stelle entschieden.
|
||||
pub const QUERY_GET_PENDING_CHURCH_APPLICATIONS_WITHOUT_SUPERVISOR: &str = r#"
|
||||
SELECT
|
||||
ca.id AS application_id,
|
||||
ca.office_type_id,
|
||||
ca.character_id AS applicant_character_id,
|
||||
ca.region_id,
|
||||
ca.created_at,
|
||||
cot.hierarchy_level AS office_hierarchy_level,
|
||||
cot.seats_per_region,
|
||||
50.0::float8 AS supervisor_reputation,
|
||||
COALESCE(ac.reputation, 50)::float8 AS applicant_reputation,
|
||||
COALESCE(ac.highest_church_hierarchy_ever, 0)::int AS applicant_highest_ever,
|
||||
COALESCE(t.level, 0)::int AS applicant_title_level,
|
||||
COALESCE((
|
||||
SELECT MAX(cot2.hierarchy_level)
|
||||
FROM falukant_data.church_office co2
|
||||
JOIN falukant_type.church_office_type cot2 ON cot2.id = co2.office_type_id
|
||||
WHERE co2.character_id = ac.id
|
||||
), 0)::int AS applicant_current_max_hierarchy,
|
||||
(CURRENT_DATE - ac.birthdate::date)::int AS applicant_age_days
|
||||
FROM falukant_data.church_application ca
|
||||
JOIN falukant_data.character ac ON ac.id = ca.character_id
|
||||
JOIN falukant_type.church_office_type cot ON cot.id = ca.office_type_id
|
||||
LEFT JOIN falukant_type.title t ON t.id = ac.title_of_nobility
|
||||
WHERE ca.status = 'pending'
|
||||
AND ca.supervisor_id IS NULL
|
||||
AND ca.office_type_id = $1::int
|
||||
AND ca.region_id = $2::int
|
||||
AND ca.created_at <= NOW() - INTERVAL '2 days'
|
||||
ORDER BY ca.created_at ASC;
|
||||
"#;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const QUERY_INTERIM_APPOINT_CHURCH_OFFICE: &str = r#"
|
||||
INSERT INTO falukant_data.church_office
|
||||
(office_type_id, character_id, region_id, supervisor_id, created_at, updated_at)
|
||||
@@ -3307,6 +3356,7 @@ pub const QUERY_INTERIM_APPOINT_CHURCH_OFFICE: &str = r#"
|
||||
RETURNING id, office_type_id, character_id, region_id;
|
||||
"#;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const QUERY_UPDATE_CHARACTER_HIGHEST_CHURCH_FROM_OFFICE_TYPE: &str = r#"
|
||||
UPDATE falukant_data.character c
|
||||
SET highest_church_hierarchy_ever = GREATEST(
|
||||
@@ -3317,6 +3367,7 @@ pub const QUERY_UPDATE_CHARACTER_HIGHEST_CHURCH_FROM_OFFICE_TYPE: &str = r#"
|
||||
RETURNING c.id;
|
||||
"#;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const QUERY_FIND_INTERIM_CHURCH_NPC_CANDIDATE: &str = r#"
|
||||
SELECT c.id AS character_id
|
||||
FROM falukant_data.character c
|
||||
@@ -3337,6 +3388,7 @@ pub const QUERY_FIND_INTERIM_CHURCH_NPC_CANDIDATE: &str = r#"
|
||||
LIMIT 1;
|
||||
"#;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const QUERY_REMOVE_LOWER_CHURCH_OFFICES_FOR_CHARACTER: &str = r#"
|
||||
DELETE FROM falukant_data.church_office co
|
||||
WHERE co.character_id = $1::int
|
||||
@@ -4100,4 +4152,16 @@ mod tests {
|
||||
assert!(query.contains(">= 16"), "{query}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn npc_church_promotions_wait_two_days_and_require_the_direct_lower_rank() {
|
||||
assert!(QUERY_GET_CHARACTERS_FOR_CHURCH_OFFICE
|
||||
.contains("current.hierarchy_level = target.hierarchy_level - 1"));
|
||||
for query in [
|
||||
QUERY_GET_PENDING_CHURCH_APPLICATIONS_FOR_SCORING,
|
||||
QUERY_GET_PENDING_CHURCH_APPLICATIONS_WITHOUT_SUPERVISOR,
|
||||
] {
|
||||
assert!(query.contains("created_at <= NOW() - INTERVAL '2 days'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user