heisenberg_data_processing/embedded/
mod.rs1use std::{path::PathBuf, sync::LazyLock};
2
3use polars::prelude::*;
4use serde::{Deserialize, Serialize};
5use tracing::info;
6
7#[cfg(any(test, feature = "test-data"))]
8use crate::raw::test_data::create_test_data;
9use crate::{
10 DataSource, Result,
11 processed::{generate_processed_data, save_processed_data_to_parquet},
12 raw::fetch::TempData,
13};
14
15#[macro_export]
17macro_rules! embedded_file_paths {
18 (admin) => {
19 "embedded_admin_search.parquet"
20 };
21 (place) => {
22 "embedded_place_search.parquet"
23 };
24 (metadata) => {
25 "embedded_data_metadata.json"
26 };
27}
28
29pub static ADMIN_DATA_PATH: &str = embedded_file_paths!(admin);
31pub static PLACE_DATA_PATH: &str = embedded_file_paths!(place);
32pub static METADATA_PATH: &str = embedded_file_paths!(metadata);
33
34static EMBEDDED_DIR_DEFAULT: &str = "src/data/embedded";
35
36pub static EMBEDDED_DIR: LazyLock<PathBuf> = LazyLock::new(|| {
37 std::env::var("EMBEDDED_DIR")
38 .map_or_else(|_| PathBuf::from(EMBEDDED_DIR_DEFAULT), PathBuf::from)
39});
40
41pub fn generate_embedded_dataset(data_source: DataSource) -> Result<()> {
43 #[cfg(feature = "download-data")]
44 {
45 crate::raw::fetch::download_data(&data_source)
46 .and_then(|temp_data| embed_data(temp_data, data_source))?;
47 Ok(())
48 }
49 #[cfg(all(not(feature = "download-data"), any(test, feature = "test-data")))]
50 {
51 tracing::warn!("download_data feature not enabled, falling back to test data");
52 generate_test_data_rust_code()
53 }
54
55 #[cfg(all(not(feature = "download-data"), not(any(test, feature = "test-data"))))]
56 {
57 compile_error!(
58 "Either 'download_data' or 'test_data' feature must be enabled to generate embedded datasets"
59 )
60 }
61}
62
63#[cfg(any(test, feature = "test-data"))]
65pub fn generate_test_data_rust_code() -> Result<()> {
66 tracing::info!("Generating embedded dataset from test data");
67
68 let test_temp_files = create_test_data();
69 embed_data(test_temp_files, DataSource::TestData)
70}
71
72fn embed_data(temp_data: TempData, data_source: DataSource) -> Result<()> {
73 info!("Generating processed data for {}", data_source);
74
75 std::fs::create_dir_all(EMBEDDED_DIR.as_path())?;
76
77 let (admin_df, place_df) = generate_processed_data(temp_data)?;
78
79 let metadata = EmbeddedMetadata::from_dfs(&admin_df, &place_df, data_source)?;
80 let metadata_path = EMBEDDED_DIR.join(METADATA_PATH);
81 metadata.write_to_file(&metadata_path)?;
82
83 let admin_path = EMBEDDED_DIR.join(ADMIN_DATA_PATH);
84 let place_path = EMBEDDED_DIR.join(PLACE_DATA_PATH);
85
86 save_processed_data_to_parquet(admin_df.clone(), &admin_path)?;
87 save_processed_data_to_parquet(place_df.clone(), &place_path)?;
88 Ok(())
89}
90
91#[derive(Clone, Debug, Deserialize, Serialize)]
92pub struct DataFrameMetadata {
93 pub rows: usize,
94 pub size_bytes: usize,
95}
96
97#[derive(Clone, Debug, Deserialize, Serialize)]
98pub struct EmbeddedMetadata {
99 pub version: String,
100 pub source: DataSource,
101 pub generated_at: String,
102 pub admin_df: DataFrameMetadata,
103 pub place_df: DataFrameMetadata,
104}
105
106impl EmbeddedMetadata {
107 pub const VERSION: &str = env!("CARGO_PKG_VERSION");
108
109 #[must_use]
110 pub fn new(
111 source: DataSource,
112 admin_rows: usize,
113 place_rows: usize,
114 admin_size_bytes: usize,
115 place_size_bytes: usize,
116 ) -> Self {
117 let admin_df_metadata = DataFrameMetadata {
118 rows: admin_rows,
119 size_bytes: admin_size_bytes,
120 };
121 let place_df_metadata = DataFrameMetadata {
122 rows: place_rows,
123 size_bytes: place_size_bytes,
124 };
125 Self {
126 version: Self::VERSION.to_string(),
127 source,
128 generated_at: chrono::Utc::now().to_rfc3339(),
129 admin_df: admin_df_metadata,
130 place_df: place_df_metadata,
131 }
132 }
133
134 pub fn from_dfs(
135 admin_df: &DataFrame,
136 place_df: &DataFrame,
137 data_source: DataSource,
138 ) -> Result<Self> {
139 Ok(Self::new(
140 data_source,
141 admin_df.height(),
142 place_df.height(),
143 admin_df.estimated_size(),
144 place_df.estimated_size(),
145 ))
146 }
147
148 pub fn to_json(&self) -> Result<String> {
149 serde_json::to_string_pretty(self).map_err(Into::into)
150 }
151
152 pub fn write_to_file(&self, path: &std::path::Path) -> Result<()> {
153 let json = self.to_json()?;
154 std::fs::write(path, json).map_err(Into::into)
155 }
156
157 pub fn load_from_file(path: &std::path::Path) -> Result<Self> {
158 let content = std::fs::read_to_string(path)?;
159 serde_json::from_str(&content).map_err(Into::into)
160 }
161
162 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
163 serde_json::from_slice(bytes).map_err(Into::into)
164 }
165}