|
1 | | -use super::schema::{self, Columns, PlaceSubmission}; |
| 1 | +use super::schema::{self, Columns, OriginSubmissionCounts, PlaceSubmission}; |
2 | 2 | use crate::Result; |
3 | 3 | use geojson::JsonObject; |
4 | 4 | use rusqlite::{named_params, params, Connection, OptionalExtension}; |
@@ -70,6 +70,44 @@ pub fn select_open_and_not_revoked(conn: &Connection) -> Result<Vec<PlaceSubmiss |
70 | 70 | .map_err(Into::into) |
71 | 71 | } |
72 | 72 |
|
| 73 | +pub fn select_origin_counts_since( |
| 74 | + since: OffsetDateTime, |
| 75 | + conn: &Connection, |
| 76 | +) -> Result<Vec<OriginSubmissionCounts>> { |
| 77 | + let sql = format!( |
| 78 | + r#" |
| 79 | + SELECT |
| 80 | + {origin} AS origin, |
| 81 | + COUNT(*) AS total, |
| 82 | + SUM(CASE WHEN {closed_at} IS NULL AND {revoked} = 0 THEN 1 ELSE 0 END) AS pending, |
| 83 | + SUM(CASE WHEN {revoked} = 1 THEN 1 ELSE 0 END) AS revoked |
| 84 | + FROM {table} |
| 85 | + WHERE {created_at} >= ?1 |
| 86 | + GROUP BY {origin} |
| 87 | + ORDER BY {origin} |
| 88 | + "#, |
| 89 | + table = schema::TABLE_NAME, |
| 90 | + origin = Columns::Origin.as_str(), |
| 91 | + closed_at = Columns::ClosedAt.as_str(), |
| 92 | + revoked = Columns::Revoked.as_str(), |
| 93 | + created_at = Columns::CreatedAt.as_str(), |
| 94 | + ); |
| 95 | + let mut stmt = conn.prepare(&sql)?; |
| 96 | + let rows = stmt.query_map(params![since.format(&Rfc3339)?], |row| { |
| 97 | + Ok(OriginSubmissionCounts { |
| 98 | + origin: row.get("origin")?, |
| 99 | + total: row.get("total")?, |
| 100 | + pending: row.get("pending")?, |
| 101 | + revoked: row.get("revoked")?, |
| 102 | + }) |
| 103 | + })?; |
| 104 | + let mut res = vec![]; |
| 105 | + for row in rows { |
| 106 | + res.push(row?); |
| 107 | + } |
| 108 | + Ok(res) |
| 109 | +} |
| 110 | + |
73 | 111 | pub fn select_by_id(id: i64, conn: &Connection) -> Result<PlaceSubmission> { |
74 | 112 | let sql = format!( |
75 | 113 | r#" |
@@ -248,6 +286,7 @@ mod test { |
248 | 286 | use crate::Result; |
249 | 287 | use geojson::JsonObject; |
250 | 288 | use serde_json::Map; |
| 289 | + use time::macros::datetime; |
251 | 290 | use time::OffsetDateTime; |
252 | 291 |
|
253 | 292 | #[test] |
@@ -433,4 +472,87 @@ mod test { |
433 | 472 | ); |
434 | 473 | Ok(()) |
435 | 474 | } |
| 475 | + |
| 476 | + #[test] |
| 477 | + fn select_origin_counts_since_groups_by_origin() -> Result<()> { |
| 478 | + let conn = conn(); |
| 479 | + |
| 480 | + let insert = |origin: &str, external_id: &str| -> Result<i64> { |
| 481 | + let args = InsertArgs { |
| 482 | + origin: origin.to_string(), |
| 483 | + external_id: external_id.to_string(), |
| 484 | + lat: 1.0, |
| 485 | + lon: 2.0, |
| 486 | + category: "cafe".to_string(), |
| 487 | + name: "Place".to_string(), |
| 488 | + extra_fields: Map::new(), |
| 489 | + }; |
| 490 | + Ok(super::insert(&args, &conn)?.id) |
| 491 | + }; |
| 492 | + |
| 493 | + let square_recent_1 = insert("square", "1")?; |
| 494 | + let square_recent_2 = insert("square", "2")?; |
| 495 | + let square_old = insert("square", "3")?; |
| 496 | + let coinos_recent = insert("coinos", "1")?; |
| 497 | + let coinos_closed = insert("coinos", "2")?; |
| 498 | + let coinos_revoked = insert("coinos", "3")?; |
| 499 | + let coinos_old = insert("coinos", "4")?; |
| 500 | + |
| 501 | + for id in [square_old, coinos_old] { |
| 502 | + conn.execute( |
| 503 | + "UPDATE place_submission SET created_at = '2020-01-01T00:00:00Z' WHERE id = ?1", |
| 504 | + rusqlite::params![id], |
| 505 | + )?; |
| 506 | + } |
| 507 | + |
| 508 | + super::set_closed_at(coinos_closed, Some(datetime!(2024-06-01 00:00 UTC)), &conn)?; |
| 509 | + super::set_revoked(coinos_revoked, true, &conn)?; |
| 510 | + |
| 511 | + let _ = square_recent_1; |
| 512 | + let _ = square_recent_2; |
| 513 | + let _ = coinos_recent; |
| 514 | + |
| 515 | + let counts = super::select_origin_counts_since(datetime!(2024-01-01 00:00 UTC), &conn)?; |
| 516 | + assert_eq!( |
| 517 | + vec![ |
| 518 | + super::schema::OriginSubmissionCounts { |
| 519 | + origin: "coinos".to_string(), |
| 520 | + total: 3, |
| 521 | + pending: 1, |
| 522 | + revoked: 1, |
| 523 | + }, |
| 524 | + super::schema::OriginSubmissionCounts { |
| 525 | + origin: "square".to_string(), |
| 526 | + total: 2, |
| 527 | + pending: 2, |
| 528 | + revoked: 0, |
| 529 | + }, |
| 530 | + ], |
| 531 | + counts |
| 532 | + ); |
| 533 | + |
| 534 | + let counts = super::select_origin_counts_since(datetime!(2019-01-01 00:00 UTC), &conn)?; |
| 535 | + assert_eq!( |
| 536 | + vec![ |
| 537 | + super::schema::OriginSubmissionCounts { |
| 538 | + origin: "coinos".to_string(), |
| 539 | + total: 4, |
| 540 | + pending: 2, |
| 541 | + revoked: 1, |
| 542 | + }, |
| 543 | + super::schema::OriginSubmissionCounts { |
| 544 | + origin: "square".to_string(), |
| 545 | + total: 3, |
| 546 | + pending: 3, |
| 547 | + revoked: 0, |
| 548 | + }, |
| 549 | + ], |
| 550 | + counts |
| 551 | + ); |
| 552 | + |
| 553 | + let counts = super::select_origin_counts_since(datetime!(2030-01-01 00:00 UTC), &conn)?; |
| 554 | + assert!(counts.is_empty()); |
| 555 | + |
| 556 | + Ok(()) |
| 557 | + } |
436 | 558 | } |
0 commit comments