%22%22%22Marimo%20notebook%3A%20explore%20FHIR%20resources%20loaded%20into%20DuckDB.%22%22%22%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.19.4%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20sys%0A%20%20%20%20from%20pathlib%20import%20Path%0A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20%23%20Add%20project%20root%20(for%20%60das%2F%60%20and%20the%20%60src%60%20package)%20to%20path%0A%20%20%20%20repo_root%20%3D%20Path(__file__).parent.parent.parent%0A%20%20%20%20if%20str(repo_root)%20not%20in%20sys.path%3A%0A%20%20%20%20%20%20%20%20sys.path.insert(0%2C%20str(repo_root))%0A%0A%20%20%20%20import%20duckdb%0A%20%20%20%20import%20pandas%20as%20pd%0A%0A%20%20%20%20from%20src.constants%20import%20Schema%0A%20%20%20%20from%20src.db.duckdb_io%20import%20get_table_summary%0A%20%20%20%20return%20Schema%2C%20duckdb%2C%20get_table_summary%2C%20mo%2C%20pd%2C%20repo_root%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%20Synthetic%20data%20exploration%0A%0A%20%20%20%20This%20notebook%20explores%20data%20across%20the%20medallion%20architecture%20layers.%0A%0A%20%20%20%20**DAS%20storage%20policy%3A**%20Only%20raw%20data%20(bronze)%20and%20reporting%20data%20(gold)%20are%20persisted.%0A%20%20%20%20Silver%20layer%20transformations%20run%20in-memory%20to%20avoid%20exposing%20intermediate%20structures%0A%20%20%20%20and%20to%20allow%20flexible%20refactoring%20without%20migrations.%0A%0A%20%20%20%20-%20**Bronze**%3A%20Raw%20FHIR%20resources%20as%20parsed%20from%20source%20files%0A%20%20%20%20-%20**Silver**%3A%20Progressively%20refined%20data%3A%0A%20%20%20%20%20%20-%20sources%3A%20Cleaned%20bronze%20(same%20structure%20as%20source)%0A%20%20%20%20%20%20-%20models%3A%20Domain-modeled%20(flattened%20for%20analytics)%0A%20%20%20%20%20%20-%20domains%3A%20Unified%20model%20(multiple%20sources%20merged%2C%20not%20yet%20implemented)%0A%20%20%20%20-%20**Gold**%3A%20Aggregated%20data%20products%20for%20reporting%20and%20analysis%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20repo_root)%3A%0A%20%20%20%20default_db_path%20%3D%20repo_root%20%2F%20%22fhir.duckdb%22%0A%20%20%20%20db_path%20%3D%20mo.ui.text(%0A%20%20%20%20%20%20%20%20value%3Dstr(default_db_path)%2C%0A%20%20%20%20%20%20%20%20label%3D%22DuckDB%20database%20path%22%2C%0A%20%20%20%20%20%20%20%20full_width%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20db_path%0A%20%20%20%20return%20(db_path%2C)%0A%0A%0A%40app.cell%0Adef%20_(db_path%2C%20duckdb)%3A%0A%20%20%20%20con%20%3D%20duckdb.connect(db_path.value%2C%20read_only%3DTrue)%0A%20%20%20%20return%20(con%2C)%0A%0A%0A%40app.cell%0Adef%20_(duckdb)%3A%0A%20%20%20%20def%20get_table_names(con%3A%20duckdb.DuckDBPyConnection%2C%20schema%3A%20str)%20-%3E%20list%5Bstr%5D%3A%0A%20%20%20%20%20%20%20%20return%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20row%5B0%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20row%20in%20con.sql(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SELECT%20table_name%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20FROM%20duckdb_tables()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20WHERE%20schema_name%20%3D%20%3F%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AND%20internal%20%3D%20FALSE%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AND%20temporary%20%3D%20FALSE%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ORDER%20BY%20table_name%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20params%3D%5Bschema%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20).fetchall()%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20return%20(get_table_names%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%20Bronze%20Layer%20Summary%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Schema%2C%20con%2C%20get_table_summary%2C%20pd)%3A%0A%20%20%20%20summary%20%3D%20get_table_summary(con%2C%20Schema.BRONZE)%0A%20%20%20%20summary_df%20%3D%20(%0A%20%20%20%20%20%20%20%20pd.DataFrame(%5B%7B%22table%22%3A%20name%2C%20%22rows%22%3A%20rows%7D%20for%20name%2C%20rows%20in%20summary.items()%5D)%0A%20%20%20%20%20%20%20%20.sort_values(%5B%22rows%22%2C%20%22table%22%5D%2C%20ascending%3D%5BFalse%2C%20True%5D)%0A%20%20%20%20%20%20%20%20.reset_index(drop%3DTrue)%0A%20%20%20%20)%0A%20%20%20%20summary_df%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Schema%2C%20con%2C%20get_table_names%2C%20mo)%3A%0A%20%20%20%20bronze_table_names%20%3D%20get_table_names(con%2C%20Schema.BRONZE)%0A%20%20%20%20table%20%3D%20mo.ui.dropdown(%0A%20%20%20%20%20%20%20%20options%3Dbronze_table_names%2C%0A%20%20%20%20%20%20%20%20value%3Dbronze_table_names%5B0%5D%20if%20bronze_table_names%20else%20None%2C%0A%20%20%20%20%20%20%20%20label%3D%22Table%20to%20sample%22%2C%0A%20%20%20%20)%0A%20%20%20%20table%0A%20%20%20%20return%20(table%2C)%0A%0A%0A%40app.cell%0Adef%20_(Schema%2C%20con%2C%20mo%2C%20table)%3A%0A%20%20%20%20sample_df%20%3D%20con.sql(f'SELECT%20*%20FROM%20%7BSchema.BRONZE%7D.%22%7Btable.value%7D%22%20LIMIT%2010').df()%0A%20%20%20%20mo.ui.table(sample_df%2C%20selection%3DNone)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Schema%2C%20con%2C%20get_table_names%2C%20mo)%3A%0A%20%20%20%20silver_table_names%20%3D%20get_table_names(con%2C%20Schema.SILVER)%0A%20%20%20%20_has_silver%20%3D%20len(silver_table_names)%20%3E%200%0A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%23%23%20Silver%20Layer%0A%0A%20%20%20%20%7B%22%22%20if%20_has_silver%20else%20%22_Silver%20tables%20not%20available.%20Run%20%60python%20main.py%20--debug%60%20to%20populate%20silver%20tables%20for%20debugging._%22%7D%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%20(silver_table_names%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20silver_table_names)%3A%0A%20%20%20%20mo.stop(len(silver_table_names)%20%3D%3D%200)%0A%20%20%20%20silver_table%20%3D%20mo.ui.dropdown(%0A%20%20%20%20%20%20%20%20options%3Dsilver_table_names%2C%0A%20%20%20%20%20%20%20%20value%3Dsilver_table_names%5B0%5D%20if%20silver_table_names%20else%20None%2C%0A%20%20%20%20%20%20%20%20label%3D%22Table%20to%20sample%22%2C%0A%20%20%20%20)%0A%20%20%20%20silver_table%0A%20%20%20%20return%20(silver_table%2C)%0A%0A%0A%40app.cell%0Adef%20_(Schema%2C%20con%2C%20mo%2C%20silver_table%2C%20silver_table_names)%3A%0A%20%20%20%20mo.stop(len(silver_table_names)%20%3D%3D%200)%0A%20%20%20%20silver_sample_df%20%3D%20con.sql(%0A%20%20%20%20%20%20%20%20f'SELECT%20*%20FROM%20%7BSchema.SILVER%7D.%22%7Bsilver_table.value%7D%22%20LIMIT%2010'%0A%20%20%20%20).df()%0A%20%20%20%20mo.ui.table(silver_sample_df%2C%20selection%3DNone)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%20Gold%20Layer%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Schema%2C%20con%2C%20get_table_names%2C%20mo)%3A%0A%20%20%20%20gold_table_names%20%3D%20get_table_names(con%2C%20Schema.GOLD)%0A%20%20%20%20gold_table%20%3D%20mo.ui.dropdown(%0A%20%20%20%20%20%20%20%20options%3Dgold_table_names%2C%0A%20%20%20%20%20%20%20%20value%3Dgold_table_names%5B0%5D%20if%20gold_table_names%20else%20None%2C%0A%20%20%20%20%20%20%20%20label%3D%22Table%20to%20sample%22%2C%0A%20%20%20%20)%0A%20%20%20%20gold_table%0A%20%20%20%20return%20(gold_table%2C)%0A%0A%0A%40app.cell%0Adef%20_(Schema%2C%20con%2C%20gold_table%2C%20mo)%3A%0A%20%20%20%20gold_sample_df%20%3D%20con.sql(%0A%20%20%20%20%20%20%20%20f'SELECT%20*%20FROM%20%7BSchema.GOLD%7D.%22%7Bgold_table.value%7D%22%20LIMIT%2010'%0A%20%20%20%20).df()%0A%20%20%20%20mo.ui.table(gold_sample_df%2C%20selection%3DNone)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%23%20Gold%20Visualizations%0A%0A%20%20%20%20Uses%20%60gold.observations_per_patient%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Schema%2C%20con%2C%20mo)%3A%0A%20%20%20%20import%20plotly.graph_objects%20as%20go%0A%0A%20%20%20%20df%20%3D%20con.sql(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%20%20%20%20SELECT%20patient_id%2C%20observation_count%0A%20%20%20%20%20%20%20%20FROM%20%7BSchema.GOLD%7D.observations_per_patient%0A%20%20%20%20%20%20%20%20ORDER%20BY%20observation_count%20DESC%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20).df()%0A%0A%20%20%20%20fig%20%3D%20go.Figure(data%3D%5Bgo.Bar(y%3Ddf%5B%22observation_count%22%5D)%5D)%0A%20%20%20%20fig.update_layout(%0A%20%20%20%20%20%20%20%20title%3D%22Observations%20per%20patient%22%2C%0A%20%20%20%20%20%20%20%20xaxis_title%3D%22Patient%22%2C%0A%20%20%20%20%20%20%20%20yaxis_title%3D%22Observation%20count%22%2C%0A%20%20%20%20%20%20%20%20bargap%3D0.05%2C%0A%20%20%20%20%20%20%20%20height%3D360%2C%0A%20%20%20%20)%0A%0A%20%20%20%20mo.ui.plotly(fig)%0A%20%20%20%20return%20(df%2C)%0A%0A%0A%40app.cell%0Adef%20_(df)%3A%0A%20%20%20%20observation_counts%20%3D%20df%5B%22observation_count%22%5D%0A%20%20%20%20first_20_percent%20%3D%20len(observation_counts)%20%2F%2F%205%0A%0A%20%20%20%20top_20_percet_observations%20%3D%20sum(observation_counts%5B%3Afirst_20_percent%5D)%0A%20%20%20%20last_80_percent_observations%20%3D%20sum(observation_counts%5Bfirst_20_percent%3A%5D)%0A%0A%20%20%20%20%23%20Tests%20if%20most%20of%20the%20observations%20are%20caused%20by%20the%2020%20%25%20most%20active%20patients%0A%20%20%20%20assert%20top_20_percet_observations%20%3E%20last_80_percent_observations%2C%20(%0A%20%20%20%20%20%20%20%20f%22%7Btop_20_percet_observations%7D%20%3E%20%7Blast_80_percent_observations%7D%20not%20true%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
83234dfacf210a825a90d3d6cf4f5351