From 91b4823d5f8dbc3f2e263cfe3c4ff1979a6a95a0 Mon Sep 17 00:00:00 2001 From: Hayden B Spence <76969160+haydenbspence@users.noreply.github.com> Date: Thu, 20 Jun 2024 11:39:16 -0400 Subject: [PATCH] refactor --- .github/workflows/main.yml | 6 +- .gitignore | 5 +- CHANGELOG.md | 40 ++++ README.md | 21 +- hestia/.dockerignore | 8 + hestia/.gitignore | 8 + hestia/__init__.py | 0 hestia/app/__init__.py | 0 hestia/app/api/__init__.py | 0 hestia/app/api/endpoints/endpoint_concept.py | 15 ++ hestia/app/core/__init__.py | 0 hestia/app/db/__init__.py | 0 hestia/app/db/queries.py | 161 +++++++++++++++ hestia/app/main.py | 0 hestia/app/schemas/__init__.py | 0 hestia/app/tests/__init__.py | 0 hestia/main.py | 0 hestia/models/__init__.py | 0 hestia/models/cdm/omop_cdm.yml | 207 +++++++++++++++++++ hestia/sql/G01.sql | 10 + hestia/tests/__init__.py | 0 {tests => hestia/tests}/motherduck_test.py | 16 +- requirements.txt | 9 + setup.cfg | 0 setup.py | 0 25 files changed, 492 insertions(+), 14 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 hestia/.dockerignore create mode 100644 hestia/.gitignore create mode 100644 hestia/__init__.py create mode 100644 hestia/app/__init__.py create mode 100644 hestia/app/api/__init__.py create mode 100644 hestia/app/api/endpoints/endpoint_concept.py create mode 100644 hestia/app/core/__init__.py create mode 100644 hestia/app/db/__init__.py create mode 100644 hestia/app/db/queries.py create mode 100644 hestia/app/main.py create mode 100644 hestia/app/schemas/__init__.py create mode 100644 hestia/app/tests/__init__.py create mode 100644 hestia/main.py create mode 100644 hestia/models/__init__.py create mode 100644 hestia/models/cdm/omop_cdm.yml create mode 100644 hestia/sql/G01.sql create mode 100644 hestia/tests/__init__.py rename {tests => hestia/tests}/motherduck_test.py (50%) create mode 100644 requirements.txt create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a5188e0..8f887b1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,14 +13,14 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.10' # Ensure this matches your project's requirements - name: Install dependencies run: | python -m pip install --upgrade pip - pip install duckdb # Install the duckdb package + pip install duckdb==1.0.0 - name: Run MotherDuck Test env: MOTHERDUCK_TOKEN: ${{ secrets.MOTHERDUCK_TOKEN }} - run: python ./tests/motherduck_test.py + run: python ./hestia/tests/motherduck_test.py diff --git a/.gitignore b/.gitignore index 157afcd..2c9e937 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ +notebooks/ +data/* .ruff_cache -.old -.vscode - # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..492a0da --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,40 @@ +# Changelog + +## [0.2.0](https://github.com/OHDSI/Hestia/compare/v0.1.0...v0.2.0) (2024-03-05) + + +### Features + +* added dev container ([3ddcc41](https://github.com/OHDSI/Hestia/commit/3ddcc4104b618c622a9f8e13f0b98d4810bb89f0)) +* added dev container ([3934c53](https://github.com/OHDSI/Hestia/commit/3934c5373e806430402851f067537a45f535590c)) + + +### Bug Fixes + +* release-please set to push for main ([5682581](https://github.com/OHDSI/Hestia/commit/568258197dd56a08b451f267a46e83a100e7657e)) + + +### Documentation + +* added CONTRIBUTING.md ([54d393c](https://github.com/OHDSI/Hestia/commit/54d393c5131504d95391de3b0e2087d61b10a06a)) +* organization ([9efe22f](https://github.com/OHDSI/Hestia/commit/9efe22f9c83e70e5b1caed9e20c58dc810296d81)) +* typo ([e32f838](https://github.com/OHDSI/Hestia/commit/e32f83821de303ad17f96b1aba0bce7d6ba4d1c6)) +* update readme ([fc8f48a](https://github.com/OHDSI/Hestia/commit/fc8f48a17319725e2bcd06067b30b459f48c6297)) +* updated contrib ([2f2ead3](https://github.com/OHDSI/Hestia/commit/2f2ead3f73cd487022b13e1337f8c92770a0353f)) +* updated readme ([aaae4da](https://github.com/OHDSI/Hestia/commit/aaae4dae9e26113186b08920032dce200994201c)) +* updated README ([2919cec](https://github.com/OHDSI/Hestia/commit/2919cec4cafdc67b572405900620fd50f449b2bf)) +* updated security.md ([6272ebc](https://github.com/OHDSI/Hestia/commit/6272ebcb57ce65d7b600e8d8253f35430e3a3dd0)) +* updates ([b6c6be3](https://github.com/OHDSI/Hestia/commit/b6c6be3502c44e79892eb3872b97ed1b055cad2d)) + +## 0.1.0 (2024-02-16) + + +### Features + +* added setup.cfg ([7aff036](https://github.com/OHDSI/Hestia/commit/7aff036b5b1e437220c725532d063eccddb97a8c)) + + +### Documentation + +* added code owner ([887b7df](https://github.com/OHDSI/Hestia/commit/887b7dfa3e494c21e84d548df19e1ddc5e09eaf0)) +* added templates and file structure ([3d33b3d](https://github.com/OHDSI/Hestia/commit/3d33b3d399144224116690ef1ddc215f229f3413)) diff --git a/README.md b/README.md index c205fad..f180060 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,21 @@ Hestia is an implementation of the OpenAPI standard to the OMOP CDM and OHDSI to ## Design +## Technology + +| Name | Licence | +|------|---------| +| [FastAPI](https://fastapi.tiangolo.com/) | MIT | +| [Ibis](https://ibis-project.org/) | Apache License 2.0 | +| [Pydantic](https://docs.pydantic.dev/latest/) | MIT | +| [Duckdb](https://github.com/duckdb/duckdb) | MIT | +| [Pytest](https://github.com/pytest-dev/pytest) | MIT | +| [Ruff](https://github.com/astral-sh/ruff) | MIT | +| [Docker Engine](https://docs.docker.com/engine/) | Apache License 2.0 | +| [FastUI](https://github.com/pydantic/FastUI) | MIT| + + + ## Installation ```bash @@ -49,7 +64,11 @@ Pre-alpha - Please read our [CONTRIBUTING.md](.github/CONTRIBUTING.md) document. -- We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.2/) to assist with change log automation, please use this style as much as possible. + +## References + +- [zhanymkanov/fastapi-best-practices](https://github.com/zhanymkanov/fastapi-best-practices?tab=readme-ov-file#project-structure) +- [tiangolo/full-stack-fastapi-template](https://github.com/tiangolo/full-stack-fastapi-template) ## License Hestia is licensed under [Apache License 2.0](./LICENSE) \ No newline at end of file diff --git a/hestia/.dockerignore b/hestia/.dockerignore new file mode 100644 index 0000000..3f4ef51 --- /dev/null +++ b/hestia/.dockerignore @@ -0,0 +1,8 @@ +# Python +__pycache__ +app.egg-info +*.pyc +.mypy_cache +.coverage +htmlcov +.venv \ No newline at end of file diff --git a/hestia/.gitignore b/hestia/.gitignore new file mode 100644 index 0000000..101092a --- /dev/null +++ b/hestia/.gitignore @@ -0,0 +1,8 @@ +__pycache__ +app.egg-info +*.pyc +.mypy_cache +.coverage +htmlcov +.cache +.venv \ No newline at end of file diff --git a/hestia/__init__.py b/hestia/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hestia/app/__init__.py b/hestia/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hestia/app/api/__init__.py b/hestia/app/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hestia/app/api/endpoints/endpoint_concept.py b/hestia/app/api/endpoints/endpoint_concept.py new file mode 100644 index 0000000..94a7572 --- /dev/null +++ b/hestia/app/api/endpoints/endpoint_concept.py @@ -0,0 +1,15 @@ +# app/api/endpoints/endpoint_concept.py + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from app.crud import crud_concept +from app.db.session import get_db # Assuming you have a function to get the DB session + +router = APIRouter() + +@router.get("/concept/{concept_id}") +def read_concept(concept_id: int, db: Session = Depends(get_db)): + db_concept = crud_concept.get_concept(db, concept_id=concept_id) + if db_concept is None: + raise HTTPException(status_code=404, detail="Concept not found") + return db_concept diff --git a/hestia/app/core/__init__.py b/hestia/app/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hestia/app/db/__init__.py b/hestia/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hestia/app/db/queries.py b/hestia/app/db/queries.py new file mode 100644 index 0000000..68bed51 --- /dev/null +++ b/hestia/app/db/queries.py @@ -0,0 +1,161 @@ + +### CARE SITE ### + + + +### CONDITION ### + +### CONDITION ERA ### + +### CONDITION OCCURRENCE COMBINATIONS ### + + + + +### DRUG ### + +### DRUG COST ### + +### DRUG ERA ### + +### DRUG EXPOSURE ### + +### OBSERVATION PERIOD ### + +### OBSERVATION ### + +### PAYER PLAN ### + +### PROCEDURE ### + +### PERSON ### + + + + +### GENERAL ### + + + +# G01: Find concept by concept ID +def find_concept_by_concept_id(concept_id, connection): + """ + This is the most generic look-up for obtaining concept details associated with a concept identifier. The query is intended as a tool for quick reference for the name, class, level and source vocabulary details associated with a concept identifier. + + Parameters: + concept_id (int): The ID of the concept to retrieve. + connection: An ibis backend connection object to the OMOP CDM database using the ibis framework. + + Returns: + DataFrame: The result of the query is an Ibis DataFrame. + """ + + query = ( + connection.table('concept').sql(f""" + SELECT + c.concept_id, + c.concept_name, + c.concept_code, + c.concept_class_id, + c.standard_concept, + c.vocabulary_id + FROM concept AS c + WHERE concept_id = {concept_id} + ; + """) + ) + + return query + + +# G02: Find a concept by code from a source vocabulary +def find_concept_by_code_form_a_source_vocabulary(concept_id, vocabulary_id, connection): + """ + This query obtains the concept details associated with a concept code, such as name, class, level and source vocabulary. + + Only concepts from the Standard Vocabularies can be searched using this query. If you want to translate codes from other Source Vocabularies to Standard Vocabularies use G06 query. + + Parameters: + concept_id (int): The ID of the concept to retrieve. + vocabulary_id (str): The source vocabulary ID of the concept to retrieve. + connection: An ibis backend connection object to the OMOP CDM database using the ibis framework. + + Returns: + DataFrame: The result of the query is an Ibis DataFrame. + """ + + query = ( + connection.table('concept').sql(f""" + SELECT + c.concept_id, + c.concept_name, + c.concept_code, + c.concept_class_id, + c.vocabulary_id + FROM concept AS c + WHERE + c.concept_code = {concept_id} AND + c.vocabulary_id = {vocabulary_id} AND + c.invalid_reason IS NULL + ; + """) + ) + + return query + + +# G04: Find synonyms for a given concept +def find_synonyms_for_a_given_concept(): + + +# G05: Translate a code from a source to a standard vocabulary. +def translate_a_code_from_a_source_to_a_standard_vocabulary(): + + +# G06: Find concepts and their descendants that are covered by a given source code +def find_concepts_and_their_descendants_that_are_covered_by_a_given_source_code(): + + +# G07: Find concepts that have a relationship with a given concept +def find_concepts_that_have_a_relationship_with_a_given_concept(): + + +# G08: Find ancestors for a given concept +def find_ancestors_for_a_given_concept(): + + +# G09: Find descendants for a given concept +def find_descendants_for_a_given_concept(): + + +# G10: Find parents for a given concept +def find_parents_for_a_given_concept(): + + +# G11: Find children for a given concept +def find_children_for_a_given_concept(): + + +# G12: List current vocabulary release number +def list_current_vocabulary_release_number(): + + +# G13: List available vocabularies +def list_available_vocabularies(): + + +# G14: Statistics about relationships between concepts +def statistics_about_relationships_between_concepts(): + + +# G15: Statistic about Concepts, Vocabularies, Classes and Levels +def statistic_about_concepts_vocabularies_classes_and_levels(): + + +# G16: Statistics about Condition Mapping of Source Vocabularies +def statistics_about_condition_mapping_of_source_vocabularies(): + + +# G17: Statistics about Drugs Mapping of Source Vocabularies +def statistics_about_drugs_mapping_of_source_vocabularies(): + diff --git a/hestia/app/main.py b/hestia/app/main.py new file mode 100644 index 0000000..e69de29 diff --git a/hestia/app/schemas/__init__.py b/hestia/app/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hestia/app/tests/__init__.py b/hestia/app/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hestia/main.py b/hestia/main.py new file mode 100644 index 0000000..e69de29 diff --git a/hestia/models/__init__.py b/hestia/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hestia/models/cdm/omop_cdm.yml b/hestia/models/cdm/omop_cdm.yml new file mode 100644 index 0000000..9a56676 --- /dev/null +++ b/hestia/models/cdm/omop_cdm.yml @@ -0,0 +1,207 @@ +id: https://w3id.org/omop +name: omopcdm +description: |- + Information about people +license: https://creativecommons.org/publicdomain/zero/1.0/ + +prefixes: + linkml: https://w3id.org/linkml/ + person: https://w3id.org/linkml/examples/person + omop: https://w3id.org/omop/ +imports: + - linkml:types +prefixes: +default_prefix: person +default_range: string + +classes: + CONCEPT: + class_uri: schema:Concept + slots: + - CONCEPT_ID + - CONCEPT_NAME + - DOMAIN_ID + - VOCABULARY_ID + - CONCEPT_CLASS_ID + - STANDARD_CONCEPT + - CONCEPT_CODE + - VALID_START_DATE + - VALID_END_DATE + - INVALID_REASON + + PERSON: + class_uri: schema:Person + slots: + - PERSON_ID + - GENDER_CONCEPT_ID + - YEAR_OF_BIRTH + - MONTH_OF_BIRTH + - DAY_OF_BIRTH + - BIRTH_DATETIME + - RACE_CONCEPT_ID + - ETHNICITY_CONCEPT_ID + - LOCATION_ID + - PROVIDER_ID + - CARE_SITE_ID + - PERSON_SOURCE_VALUE + - GENDER_SOURCE_VALUE + - GENDER_SOURCE_CONCEPT_ID + - RACE_SOURCE_VALUE + - RACE_SOURCE_CONCEPT_ID + - ETHNICITY_SOURCE_VALUE + - ETHNICITY_SOURCE_CONCEPT_ID + + Container: + tree_root: true + attributes: + persons: + multivalued: true + inlined_as_list: true + range: Person + +slots: + + # CONCEPT + CONCEPT_ID: + aliases: + - concept + range: integer + required: true + identifier: true + description: A unique identifier for each Concept across all domains. + CONCEPT_NAME: + range: integer + required: true + identifier: false + description: An unambiguous, meaningful and descriptive name for the Concept. + DOMAIN_ID: + range: integer + required: true + identifier: false + description: A foreign key to the DOMAIN table the Concept belongs to. + VOCABULARY_ID: + range: integer + required: true + identifier: false + description: A foreign key to the VOCABULARY table indicating from which source the Concept has been adapted. + CONCEPT_CLASS_ID: + range: integer + required: true + identifier: false + description: The attribute or concept class of the Concept. Examples are "Clinical Drug", "Ingredient", "Clinical Finding" etc. + STANDARD_CONCEPT: + range: integer + required: true + identifier: false + description: This flag determines where a Concept is a Standard Concept, i.e. is used in the data, a Classification Concept, or a non-standard Source Concept. The allowable values are "S" (Standard Concept) and "C" (Classification Concept), otherwise the content is NULL. + CONCEPT_CODE: + range: integer + required: true + identifier: false + description: The concept code represents the identifier of the Concept in the source vocabulary, such as SNOMED-CT concept IDs, RxNorm RXCUIs etc. Note that concept codes are not unique across vocabularies. + VALID_START_DATE: + range: integer + required: true + identifier: false + description: The date when the Concept was first recorded. The default value is 1-Jan-1970, meaning, the Concept has no (known) date of inception. + VALID_END_DATE: + range: integer + required: true + identifier: false + description: The date when the Concept became invalid because it was deleted or superseded (updated) by a new concept. The default value is 31-Dec-2099, meaning, the Concept is valid until it becomes deprecated. + INVALID_REASON: + range: integer + required: true + identifier: false + description: Reason the Concept was invalidated. Possible values are D (deleted), U (replaced with an update) or NULL when valid_end_date has the default value. + + # PERSON + PERSON_ID: + range: integer + required: true + identifier: true + description: It is assumed that every person with a different unique identifier is in fact a different person and should be treated independently. + comments: Any person linkage that needs to occur to uniquely identify Persons ought to be done prior to writing this table. This identifier can be the original id from the source data provided if it is an integer, otherwise it can be an autogenerated number. + GENDER_CONCEPT_ID: + range: integer + required: true + description: This field is meant to capture the biological sex at birth of the Person. This field should not be used to study gender identity issues. + comments: Use the gender or sex value present in the data under the assumption that it is the biological sex at birth. If the source data captures gender identity it should be stored in the OBSERVATION table. + pattern: \b(8532|8507)\b + YEAR_OF_BIRTH: + range: integer + required: true + description: Compute age using year_of_birth. + comments: For data sources with date of birth, the year should be extracted. For data sources where the year of birth is not available, the approximate year of birth could be derived based on age group categorization, if available. + minimum_value: 1800 + maximum_value: 2200 + MONTH_OF_BIRTH: + range: integer + description: + comments: For data sources that provide the precise date of birth, the month should be extracted and stored in this field. + minimum_value: 1 + maximum_value: 12 + DAY_OF_BIRTH: + range: integer + description: + comments: For data sources that provide the precise date of birth, the day should be extracted and stored in this field. + minimum_value: 1 + maximum_value: 31 + BIRTH_DATETIME: + range: datetime + description: + comments: This field is not required but highly encouraged. For data sources that provide the precise datetime of birth, that value should be stored in this field. If birth_datetime is not provided in the source, use the following logic to infer the date. If day_of_birth is null and month_of_birth is not null then use the first of the month in that year. If month_of_birth is null or if day_of_birth AND month_of_birth are both null and the person has records during their year of birth then use the date of the earliest record, otherwise use the 15th of June of that year. If time of birth is not given use midnight (00:00:0000). + RACE_CONCEPT_ID: + range: integer + required: true + description: This field captures race or ethnic background of the person. + comments: Only use this field if you have information about race or ethnic background. The Vocabulary contains Concepts about the main races and ethnic backgrounds in a hierarchical system. Due to the imprecise nature of human races and ethnic backgrounds, this is not a perfect system. Mixed races are not supported. If a clear race or ethnic background cannot be established, use Concept_Id 0. Accepted Race Concepts. + ETHNICITY_CONCEPT_ID: + range: integer + required: true + description: This field captures Ethnicity as defined by the Office of Management and Budget (OMB) of the US Government. It distinguishes only between “Hispanic” and “Not Hispanic”. Races and ethnic backgrounds are not stored here. + comments: Only use this field if you have US-based data and a source of this information. Do not attempt to infer Ethnicity from the race or ethnic background of the Person. Accepted ethnicity concepts + LOCATION_ID: + range: integer + required: true + description: The location refers to the physical address of the person. This field should capture the last known location of the person. + comments: Put the location_id from the LOCATION table here that represents the most granular location information for the person. This could represent anything from postal code or parts thereof, state, or county for example. Since many databases contain deidentified data, it is common that the precision of the location is reduced to prevent re-identification. This field should capture the last known location. + PROVIDER_ID: + range: integer + required: true + description: The Provider refers to the last known primary care provider (General Practitioner). + comments: Put the provider_id from the PROVIDER table of the last known general practitioner of the person. If there are multiple providers, it is up to the ETL to decide which to put here. + CARE_SITE_ID: + range: integer + required: true + description: The Care Site refers to where the Provider typically provides the primary care. + comments: + PERSON_SOURCE_VALUE: + range: string + description: Use this field to link back to persons in the source data. This is typically used for error checking of ETL logic. + comments: Some use cases require the ability to link back to persons in the source data. This field allows for the storing of the person value as it appears in the source. This field is not required but strongly recommended. + GENDER_SOURCE_VALUE: + range: string + description: This field is used to store the biological sex of the person from the source data. It is not intended for use in standard analytics but for reference only. + comments: Put the biological sex of the person as it appears in the source data. + GENDER_SOURCE_CONCEPT_ID: + range: integer + description: Due to the small number of options, this tends to be zero. + comments: If the source data codes biological sex in a non-standard vocabulary, store the concept_id here. + RACE_SOURCE_VALUE: + range: string + description: This field is used to store the race of the person from the source data. It is not intended for use in standard analytics but for reference only. + comments: Put the race of the person as it appears in the source data. + RACE_SOURCE_CONCEPT_ID: + range: integer + description: Due to the small number of options, this tends to be zero. + comments: If the source data codes race in an OMOP supported vocabulary store the concept_id here. + ETHNICITY_SOURCE_VALUE: + range: string + description: This field is used to store the ethnicity of the person from the source data. It is not intended for use in standard analytics but for reference only. + comments: If the person has an ethnicity other than the OMB standard of “Hispanic” or “Not Hispanic” store that value from the source data here. + ETHNICITY_SOURCE_CONCEPT_ID: + range: integer + description: Due to the small number of options, this tends to be zero. + comments: If the source data codes ethnicity in an OMOP supported vocabulary, store the concept_id here. + diff --git a/hestia/sql/G01.sql b/hestia/sql/G01.sql new file mode 100644 index 0000000..b4bd0f0 --- /dev/null +++ b/hestia/sql/G01.sql @@ -0,0 +1,10 @@ +SELECT + c.concept_id, + c.concept_name, + c.concept_code, + c.concept_class_id, + c.standard_concept, + c.vocabulary_id +FROM @vocab.concept AS c +WHERE c.concept_id = 192671 +; \ No newline at end of file diff --git a/hestia/tests/__init__.py b/hestia/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/motherduck_test.py b/hestia/tests/motherduck_test.py similarity index 50% rename from tests/motherduck_test.py rename to hestia/tests/motherduck_test.py index 0007cd7..70d5cfd 100644 --- a/tests/motherduck_test.py +++ b/hestia/tests/motherduck_test.py @@ -8,19 +8,21 @@ raise ValueError("MOTHERDUCK_TOKEN is not set in the environment variables.") # Create a connection to MotherDuck -conn = duckdb.connect(database=':memory:', access_mode='read_write', config={"access_token": motherduck_token}) +conn = duckdb.connect +conn = duckdb.connect(f'md:test_db?motherduck_token={motherduck_token}') # Create a test table -conn.execute("CREATE TABLE test_table (id INTEGER, name VARCHAR)") - -# Insert data into the table -conn.execute("INSERT INTO test_table VALUES (1, 'Alice'), (2, 'Bob')") +conn.execute(""" + DROP TABLE IF EXISTS test_tbl; + CREATE TABLE test_tbl (id INTEGER, test_title VARCHAR, test_description VARCHAR); + INSERT INTO test_tbl VALUES (1, 'Insert Test', 'Test inserting data into a table'); + """) # Query the table -result = conn.execute("SELECT * FROM test_table").fetchall() +result = conn.execute("SELECT * FROM test_tbl").fetchall() # Check the results -if result == [(1, 'Alice'), (2, 'Bob')]: +if result == [(1, 'Insert Test', 'Test inserting data into a table')]: print("Test Passed: Data matches expected results") else: raise Exception("Test Failed: Data does not match expected results") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..d1d4bb5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +jupyter +pydantic +fastapi +fastui +uvicorn[standard] +linkml +duckdb==1.0.0 +pandas +polars \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..e69de29 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e69de29