Skip to content

Commit 50a6207

Browse files
authored
simplified sqlite script (#3)
1 parent d58e714 commit 50a6207

15 files changed

+736
-68
lines changed

sqlite/README.md

Lines changed: 0 additions & 23 deletions
This file was deleted.
File renamed without changes.

sqlite_basic/README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Elimity Insights example SQLite connector
2+
3+
This Python package implements a basic connector importing data from a SQLite (version 3.37.0 or
4+
higher) database.
5+
6+
Note: For larger datasets we recommend avoiding loading all data into memory at once.
7+
[This](../sqlite_streaming) example connector extends the basic example with streaming to avoid this.
8+
9+
## Example data
10+
11+
This example script expects the database to contain users and roles, both with metadata.
12+
13+
You can use the following query to initialize an example database:
14+
15+
```sqlite
16+
BEGIN;
17+
18+
CREATE TABLE users (
19+
display_name TEXT NOT NULL,
20+
first_name TEXT NOT NULL,
21+
id INTEGER PRIMARY KEY,
22+
last_name TEXT NOT NULL,
23+
last_logon INTEGER
24+
) STRICT;
25+
26+
INSERT INTO users (display_name, first_name, last_name, last_logon)
27+
VALUES
28+
('dduck', 'Donald', 'Duck', 1668603363),
29+
('mmouse', 'Mickey', 'Mouse', NULL);
30+
CREATE TABLE roles (
31+
id INTEGER PRIMARY KEY,
32+
name TEXT NOT NULL,
33+
security_level INTEGER NOT NULL
34+
) STRICT;
35+
36+
INSERT INTO roles (name, security_level)
37+
VALUES
38+
('Reader', 0),
39+
('Writer', 1);
40+
41+
CREATE TABLE user_roles (
42+
id INTEGER PRIMARY KEY,
43+
role_id INTEGER NOT NULL REFERENCES roles (id),
44+
user_id INTEGER NOT NULL REFERENCES users (id),
45+
UNIQUE (role_id, user_id)
46+
) STRICT;
47+
48+
INSERT INTO user_roles (role_id, user_id)
49+
VALUES
50+
(1, 1),
51+
(1, 2),
52+
(2, 2);
53+
54+
COMMIT
55+
```
56+
57+
## Data model for Elimity Insights
58+
59+
The data model for a custom source in Elimity Insights can be found in the [`data-model.json`](data-model.json) file.
File renamed without changes.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
from datetime import datetime
2+
from sqlite3 import connect
3+
from typing import List
4+
5+
from elimity_insights_client import (
6+
AttributeAssignment,
7+
Client,
8+
Config,
9+
DateTime,
10+
DateTimeValue,
11+
DomainGraph,
12+
Entity,
13+
NumberValue,
14+
Relationship,
15+
StringValue,
16+
)
17+
18+
# Configuration
19+
# Note: If you want, you can turn these variables into command line inputs using [https://docs.python.org/3/library/argparse.html](ArgParse).
20+
# See the example connector `sqlite_streaming` for an example of how to do this.
21+
DATABASE = "mydb.sqlite3"
22+
ELIMITY_INSIGHTS_URL = "https://example.elimity.com"
23+
ELIMITY_INSIGHTS_SOURCE_ID = "1"
24+
ELIMITY_INSIGHTS_SOURCE_API_TOKEN = "34kj328ukj291ukjsdkj394ukjdf"
25+
26+
27+
def main() -> None:
28+
config = Config(
29+
ELIMITY_INSIGHTS_SOURCE_ID,
30+
ELIMITY_INSIGHTS_URL,
31+
ELIMITY_INSIGHTS_SOURCE_API_TOKEN,
32+
)
33+
client = Client(config)
34+
with connect(DATABASE, isolation_level=None) as connection:
35+
36+
connection.execute("BEGIN")
37+
38+
# To load the data of the database into Elimity Insights, we have to construct a "domain graph".
39+
# Essentially, this domain graph contains the list of entities and their attribute assignments,
40+
# and the relationships between these entities.
41+
entities: List[Entity] = []
42+
relationships: List[Relationship] = []
43+
44+
# Step 1. Turn the users into entities with attribute assignments.
45+
for display_name, first_name, id, last_logon, last_name in connection.execute(
46+
"""
47+
SELECT display_name, first_name, CAST(id AS TEXT), last_logon, last_name
48+
FROM users
49+
"""
50+
):
51+
user_assignments: List[AttributeAssignment] = [
52+
AttributeAssignment("firstName", StringValue(first_name)),
53+
AttributeAssignment("lastName", StringValue(last_name)),
54+
]
55+
if last_logon is not None:
56+
last_logon_datetime = datetime.utcfromtimestamp(last_logon)
57+
last_logon_time = DateTime(
58+
last_logon_datetime.year,
59+
last_logon_datetime.month,
60+
last_logon_datetime.day,
61+
last_logon_datetime.hour,
62+
last_logon_datetime.minute,
63+
last_logon_datetime.second,
64+
)
65+
last_logon_value = DateTimeValue(last_logon_time)
66+
user_assignments.append(
67+
AttributeAssignment("lastLogon", last_logon_value)
68+
)
69+
entities.append(Entity(user_assignments, id, display_name, "user"))
70+
71+
# Step 2. Turn the roles into entities with attribute assignments
72+
for id, name, security_level in connection.execute(
73+
"""
74+
SELECT CAST(id AS TEXT), name, security_level
75+
FROM roles
76+
"""
77+
):
78+
role_assignments: List[AttributeAssignment] = [
79+
AttributeAssignment("securityLevel", NumberValue(security_level))
80+
]
81+
entities.append(Entity(role_assignments, id, name, "role"))
82+
83+
# Step 3. Turn the relationships table into relationships
84+
for user_id, role_id in connection.execute(
85+
"""
86+
SELECT CAST(user_id AS TEXT), CAST(role_id AS TEXT)
87+
FROM user_roles
88+
"""
89+
):
90+
# Relationships can be assigned attributes as well, but this example does not include that.
91+
assignments: List[AttributeAssignment] = []
92+
relationships.append(
93+
Relationship(assignments, user_id, "user", role_id, "role")
94+
)
95+
96+
# Step 4. Upload the entities and relationships as a domain graph
97+
graph = DomainGraph(entities, relationships)
98+
client.reload_domain_graph(graph)
File renamed without changes.
File renamed without changes.
File renamed without changes.

sqlite_streaming/.isort.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[settings]
2+
profile = black

sqlite_streaming/README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Elimity Insights example SQLite connector with data streaming
2+
3+
This Python package implements an example connector importing data from a SQLite (version 3.37.0 or
4+
higher) database.
5+
6+
This example extends [the basic example](../sqlite_basic) with data streaming
7+
to avoid that all data is loaded into memory at once, which is useful for large datasets.
8+
9+
In addition, this example illustrates how to receive input parameters from the command line
10+
using [ArgParse](https://docs.python.org/3/library/argparse.html).
11+
12+
## Usage
13+
14+
```console
15+
(venv) $ elimity-insights-example-sqlite-connector --help
16+
usage: elimity-insights-example-sqlite-connector [-h] --database DATABASE --source-id SOURCE_ID --source-token SOURCE_TOKEN --url URL
17+
18+
Example Elimity Insights custom connector importing from a SQLite database
19+
20+
optional arguments:
21+
-h, --help show this help message and exit
22+
--database DATABASE path to the SQLite database file
23+
--source-id SOURCE_ID
24+
identifier for authenticating the source in Elimity Insights
25+
--source-token SOURCE_TOKEN
26+
token for authenticating the source in Elimity Insights
27+
--url URL URL of the Elimity Insights server
28+
```
29+
30+
## Example data
31+
32+
This example script expects the database to contain users and roles, both with metadata.
33+
34+
You can use the following query to initialize an example database:
35+
36+
```sqlite
37+
BEGIN;
38+
39+
CREATE TABLE users (
40+
display_name TEXT NOT NULL,
41+
first_name TEXT NOT NULL,
42+
id INTEGER PRIMARY KEY,
43+
last_name TEXT NOT NULL,
44+
last_logon INTEGER
45+
) STRICT;
46+
47+
INSERT INTO users (display_name, first_name, last_name, last_logon)
48+
VALUES
49+
('dduck', 'Donald', 'Duck', 1668603363),
50+
('mmouse', 'Mickey', 'Mouse', NULL);
51+
CREATE TABLE roles (
52+
id INTEGER PRIMARY KEY,
53+
name TEXT NOT NULL,
54+
security_level INTEGER NOT NULL
55+
) STRICT;
56+
57+
INSERT INTO roles (name, security_level)
58+
VALUES
59+
('Reader', 0),
60+
('Writer', 1);
61+
62+
CREATE TABLE user_roles (
63+
id INTEGER PRIMARY KEY,
64+
role_id INTEGER NOT NULL REFERENCES roles (id),
65+
user_id INTEGER NOT NULL REFERENCES users (id),
66+
UNIQUE (role_id, user_id)
67+
) STRICT;
68+
69+
INSERT INTO user_roles (role_id, user_id)
70+
VALUES
71+
(1, 1),
72+
(1, 2),
73+
(2, 2);
74+
75+
COMMIT
76+
```
77+
78+
## Data model for Elimity Insights
79+
80+
The data model for a custom source in Elimity Insights can be found in the [`data-model.json`](data-model.json) file.

sqlite_streaming/data-model.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"entityAttributeTypes": [
3+
{
4+
"archived": false,
5+
"description": "",
6+
"entityTypeId": "user",
7+
"id": "firstName",
8+
"name": "First name",
9+
"type": "string"
10+
},
11+
{
12+
"archived": false,
13+
"description": "",
14+
"entityTypeId": "user",
15+
"id": "lastName",
16+
"name": "Last name",
17+
"type": "string"
18+
},
19+
{
20+
"archived": false,
21+
"description": "",
22+
"entityTypeId": "user",
23+
"id": "lastLogon",
24+
"name": "Last logon",
25+
"type": "dateTime"
26+
},
27+
{
28+
"archived": false,
29+
"description": "",
30+
"entityTypeId": "role",
31+
"id": "securityLevel",
32+
"name": "Security level",
33+
"type": "number"
34+
}
35+
],
36+
"entityTypes": [
37+
{
38+
"anonymized": false,
39+
"icon": "person",
40+
"id": "user",
41+
"plural": "Users",
42+
"singular": "User"
43+
},
44+
{
45+
"anonymized": false,
46+
"icon": "label",
47+
"id": "role",
48+
"plural": "Roles",
49+
"singular": "Role"
50+
}
51+
],
52+
"relationshipAttributeTypes": []
53+
}

sqlite/elimity_insights_example_sqlite_connector.py renamed to sqlite_streaming/elimity_insights_example_sqlite_connector.py

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -22,46 +22,7 @@ def main() -> None:
2222
config = Config(args.source_id, args.url, args.source_token)
2323
client = Client(config)
2424
with connect(args.database, isolation_level=None) as connection:
25-
sql = """
26-
BEGIN;
27-
28-
CREATE TABLE users (
29-
display_name TEXT NOT NULL,
30-
first_name TEXT NOT NULL,
31-
id INTEGER PRIMARY KEY,
32-
last_name TEXT NOT NULL,
33-
last_logon INTEGER
34-
) STRICT;
35-
36-
INSERT INTO users (display_name, first_name, last_name, last_logon)
37-
VALUES
38-
('dduck', 'Donald', 'Duck', 1668603363),
39-
('mmouse', 'Mickey', 'Mouse', NULL);
40-
CREATE TABLE roles (
41-
id INTEGER PRIMARY KEY,
42-
name TEXT NOT NULL,
43-
security_level INTEGER NOT NULL
44-
) STRICT;
45-
46-
INSERT INTO roles (name, security_level)
47-
VALUES
48-
('Reader', 0),
49-
('Writer', 1);
50-
51-
CREATE TABLE user_roles (
52-
id INTEGER PRIMARY KEY,
53-
role_id INTEGER NOT NULL REFERENCES roles (id),
54-
user_id INTEGER NOT NULL REFERENCES users (id),
55-
UNIQUE (role_id, user_id)
56-
) STRICT;
57-
58-
INSERT INTO user_roles (role_id, user_id)
59-
VALUES
60-
(1, 1),
61-
(1, 2),
62-
(2, 2)
63-
"""
64-
connection.executescript(sql if args.generate_database else "BEGIN")
25+
connection.execute("BEGIN")
6526
entities = _entities(connection)
6627
relationships = _relationships(connection)
6728
graph = DomainGraph(entities, relationships)
@@ -135,11 +96,6 @@ def _string_attribute_assignment(
13596
description="Example Elimity Insights custom connector importing from a SQLite database"
13697
)
13798
_add_flag("path to the SQLite database file", "--database")
138-
_parser.add_argument(
139-
"--generate-database",
140-
action="store_true",
141-
help="generate a new sample database before importing",
142-
)
14399
_add_flag(
144100
"identifier for authenticating the source in Elimity Insights", "--source-id", True
145101
)

sqlite_streaming/mypy.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[mypy]
2+
disallow_any_explicit = True
3+
strict = True

0 commit comments

Comments
 (0)