lam-cli 0.0.2__tar.gz → 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lam-cli-0.0.2/lam_cli.egg-info → lam-cli-0.0.3}/PKG-INFO +2 -1
- lam-cli-0.0.3/lam/lam.py +145 -0
- {lam-cli-0.0.2 → lam-cli-0.0.3/lam_cli.egg-info}/PKG-INFO +2 -1
- lam-cli-0.0.3/lam_cli.egg-info/requires.txt +2 -0
- {lam-cli-0.0.2 → lam-cli-0.0.3}/setup.py +2 -2
- lam-cli-0.0.2/lam/lam.py +0 -71
- lam-cli-0.0.2/lam_cli.egg-info/requires.txt +0 -1
- {lam-cli-0.0.2 → lam-cli-0.0.3}/LICENSE +0 -0
- {lam-cli-0.0.2 → lam-cli-0.0.3}/README.md +0 -0
- {lam-cli-0.0.2 → lam-cli-0.0.3}/lam/__init__.py +0 -0
- {lam-cli-0.0.2 → lam-cli-0.0.3}/lam_cli.egg-info/SOURCES.txt +0 -0
- {lam-cli-0.0.2 → lam-cli-0.0.3}/lam_cli.egg-info/dependency_links.txt +0 -0
- {lam-cli-0.0.2 → lam-cli-0.0.3}/lam_cli.egg-info/entry_points.txt +0 -0
- {lam-cli-0.0.2 → lam-cli-0.0.3}/lam_cli.egg-info/top_level.txt +0 -0
- {lam-cli-0.0.2 → lam-cli-0.0.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lam-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: Laminar data transformation tool
|
|
5
5
|
Home-page: https://github.com/laminar-run/lam
|
|
6
6
|
Author: Laminar Run, Inc.
|
|
@@ -8,6 +8,7 @@ Author-email: connect@laminar.run
|
|
|
8
8
|
License: GPLv3
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Requires-Dist: click
|
|
11
|
+
Requires-Dist: posthog
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
Laminar is a platform that makes building and maintaining API integrations faster.
|
lam-cli-0.0.3/lam/lam.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import socket
|
|
8
|
+
import subprocess
|
|
9
|
+
import threading
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from queue import Queue
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
from posthog import Posthog
|
|
15
|
+
|
|
16
|
+
posthog = Posthog(project_api_key='phc_wfeHFG0p5yZIdBpjVYy00o5x1HbEpggdMzIuFYgNPSK', host='https://app.posthog.com')
|
|
17
|
+
|
|
18
|
+
jq_path = 'jq'
|
|
19
|
+
event_queue = Queue()
|
|
20
|
+
|
|
21
|
+
def generate_distinct_id(workspace_id, flow_id):
|
|
22
|
+
user_id = os.getuid()
|
|
23
|
+
hostname = socket.gethostname()
|
|
24
|
+
return f"{user_id}_{hostname}_{workspace_id}_{flow_id}"
|
|
25
|
+
|
|
26
|
+
def track_event(event_name, properties, workspace_id="local", flow_id="local"):
|
|
27
|
+
logging.info(f"Event {event_name} triggered, with properties: {properties}")
|
|
28
|
+
event_queue.put((event_name, properties, workspace_id, flow_id))
|
|
29
|
+
|
|
30
|
+
def event_logger():
|
|
31
|
+
while True:
|
|
32
|
+
event_name, properties, workspace_id, flow_id = event_queue.get()
|
|
33
|
+
try:
|
|
34
|
+
distinct_id = generate_distinct_id(workspace_id, flow_id)
|
|
35
|
+
posthog.capture(distinct_id=distinct_id, event=event_name, properties=properties)
|
|
36
|
+
finally:
|
|
37
|
+
event_queue.task_done()
|
|
38
|
+
|
|
39
|
+
def parse_program_file(program_file):
|
|
40
|
+
logging.info(f"Parsing program file: {program_file}")
|
|
41
|
+
with open(program_file, 'r') as file:
|
|
42
|
+
return ''.join(line for line in file if not line.strip().startswith('#'))
|
|
43
|
+
|
|
44
|
+
def run_jq(jq_script, input_data):
|
|
45
|
+
logging.info(f"Running jq script {jq_script} with input data {input_data}")
|
|
46
|
+
process = subprocess.Popen([jq_path, '-c', jq_script], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
47
|
+
output, error = process.communicate(input=input_data)
|
|
48
|
+
if error:
|
|
49
|
+
logging.error(f"Error running jq: {error}")
|
|
50
|
+
return output, error
|
|
51
|
+
|
|
52
|
+
def process_input(input, workspace_id, flow_id):
|
|
53
|
+
logging.info(f"Processing input: {input}")
|
|
54
|
+
if os.path.isfile(input):
|
|
55
|
+
with open(input, 'r') as file:
|
|
56
|
+
return file.read(), None
|
|
57
|
+
try:
|
|
58
|
+
json.loads(input)
|
|
59
|
+
return input, None
|
|
60
|
+
except json.JSONDecodeError as e:
|
|
61
|
+
logging.error(f"Invalid JSON input: {e}")
|
|
62
|
+
track_event('lam.run.error', {'error': f"Invalid JSON input: {e}", 'workspace_id': workspace_id, 'flow_id': flow_id}, workspace_id, flow_id)
|
|
63
|
+
return None, str(e)
|
|
64
|
+
|
|
65
|
+
def handle_jq_output(output, as_json, workspace_id, flow_id):
|
|
66
|
+
logging.info(f"Handling jq output: {output}")
|
|
67
|
+
try:
|
|
68
|
+
json_output = json.loads(output)
|
|
69
|
+
# Make sure the output has a top-level object
|
|
70
|
+
if not isinstance(json_output, dict):
|
|
71
|
+
track_event('lam.run.warn', {'error': 'Invalid JSON output', 'workspace_id': workspace_id, 'flow_id': flow_id}, workspace_id, flow_id)
|
|
72
|
+
return {"lam.result": json_output} if as_json else output, None
|
|
73
|
+
return json_output if as_json else output, None
|
|
74
|
+
except json.JSONDecodeError as e:
|
|
75
|
+
logging.error("Failed to parse JSON output, may be multiple JSON objects. Attempting to parse as JSON lines.")
|
|
76
|
+
track_event('lam.run.warn', {'error': f"Invalid JSON output: {e}", 'workspace_id': workspace_id, 'flow_id': flow_id}, workspace_id, flow_id)
|
|
77
|
+
if as_json:
|
|
78
|
+
json_objects = [json.loads(line) for line in output.strip().split('\n') if line]
|
|
79
|
+
return {"lam.concatenated": json_objects}, None
|
|
80
|
+
return output, "Failed to parse JSON output."
|
|
81
|
+
|
|
82
|
+
def write_to_result_file(result, result_file):
|
|
83
|
+
with open(result_file, 'w') as file:
|
|
84
|
+
file.write(json.dumps(result, indent=4))
|
|
85
|
+
|
|
86
|
+
@click.group()
|
|
87
|
+
def lam():
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
@lam.command()
|
|
91
|
+
@click.argument('program_file', type=click.Path(exists=True))
|
|
92
|
+
@click.argument('input', type=str)
|
|
93
|
+
@click.option('--workspace_id', default="local", help="Workspace ID")
|
|
94
|
+
@click.option('--flow_id', default="local", help="Flow ID")
|
|
95
|
+
@click.option('--as-json', is_flag=True, default=True, help="Output as JSON")
|
|
96
|
+
def run(program_file, input, workspace_id, flow_id, as_json):
|
|
97
|
+
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
98
|
+
log_file = f"lam_run_{workspace_id}_{flow_id}_{timestamp}.log"
|
|
99
|
+
result_file = f"lam_result_{workspace_id}_{flow_id}_{timestamp}.json"
|
|
100
|
+
|
|
101
|
+
# Now configure logging with the determined log file name
|
|
102
|
+
logging.basicConfig(level=logging.INFO, filename=log_file, filemode='w',
|
|
103
|
+
format='%(asctime)s - %(levelname)s - %(message)s')
|
|
104
|
+
|
|
105
|
+
logging.info(f"Logging to {log_file}")
|
|
106
|
+
logging.info(f"Running command with program file: {program_file}, input: {input}, workspace_id: {workspace_id}, flow_id: {flow_id}, as_json: {as_json}")
|
|
107
|
+
if not shutil.which("jq"):
|
|
108
|
+
logging.error("Unable to find jq, killing process")
|
|
109
|
+
click.echo({"lam.error": "jq is not installed"}, err=True)
|
|
110
|
+
track_event('lam.run.error', {'error': 'jq is not installed', 'workspace_id': workspace_id, 'flow_id': flow_id}, workspace_id, flow_id)
|
|
111
|
+
write_to_result_file({"lam.error": "jq is not installed"}, result_file)
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
input_data, error = process_input(input, workspace_id, flow_id)
|
|
115
|
+
if error:
|
|
116
|
+
click.echo({"lam.error": f"Invalid input: {error}"}, err=True)
|
|
117
|
+
track_event('lam.run.error', {'error': f"Invalid input: {error}", 'workspace_id': workspace_id, 'flow_id': flow_id}, workspace_id, flow_id)
|
|
118
|
+
write_to_result_file({"lam.error": f"Invalid input: {error}"}, result_file)
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
jq_script = parse_program_file(program_file)
|
|
122
|
+
track_event('lam.run.start', {'program_file': program_file, 'as_json': as_json, 'workspace_id': workspace_id, 'flow_id': flow_id}, workspace_id, flow_id)
|
|
123
|
+
output, jq_error = run_jq(jq_script, input_data)
|
|
124
|
+
|
|
125
|
+
if jq_error:
|
|
126
|
+
click.echo({"lam.error": jq_error}, err=True)
|
|
127
|
+
track_event('lam.run.run_jq_error', {'error': jq_error, 'workspace_id': workspace_id, 'flow_id': flow_id}, workspace_id, flow_id)
|
|
128
|
+
write_to_result_file({"lam.error": jq_error}, result_file)
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
result, error = handle_jq_output(output, as_json, workspace_id, flow_id)
|
|
132
|
+
if error:
|
|
133
|
+
click.echo({"lam.error": error}, err=True)
|
|
134
|
+
track_event('lam.run.handle_jq_output_error', {'error': error, 'workspace_id': workspace_id, 'flow_id': flow_id}, workspace_id, flow_id)
|
|
135
|
+
write_to_result_file({"lam.error": error}, result_file)
|
|
136
|
+
else:
|
|
137
|
+
click.echo(json.dumps(result, indent=4) if as_json else result)
|
|
138
|
+
track_event('lam.run.success', {'workspace_id': workspace_id, 'flow_id': flow_id}, workspace_id, flow_id)
|
|
139
|
+
write_to_result_file(result, result_file)
|
|
140
|
+
|
|
141
|
+
logging.info("Run complete, waiting for event logger to finish")
|
|
142
|
+
|
|
143
|
+
if __name__ == '__main__':
|
|
144
|
+
threading.Thread(target=event_logger, daemon=True).start()
|
|
145
|
+
lam()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lam-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: Laminar data transformation tool
|
|
5
5
|
Home-page: https://github.com/laminar-run/lam
|
|
6
6
|
Author: Laminar Run, Inc.
|
|
@@ -8,6 +8,7 @@ Author-email: connect@laminar.run
|
|
|
8
8
|
License: GPLv3
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Requires-Dist: click
|
|
11
|
+
Requires-Dist: posthog
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
Laminar is a platform that makes building and maintaining API integrations faster.
|
|
@@ -2,11 +2,11 @@ from setuptools import find_packages, setup
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name='lam-cli',
|
|
5
|
-
version='0.0.
|
|
5
|
+
version='0.0.3',
|
|
6
6
|
packages=find_packages(),
|
|
7
7
|
install_requires=[
|
|
8
8
|
'click',
|
|
9
|
-
|
|
9
|
+
'posthog',
|
|
10
10
|
],
|
|
11
11
|
entry_points={
|
|
12
12
|
'console_scripts': [
|
lam-cli-0.0.2/lam/lam.py
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import os
|
|
5
|
-
import shutil
|
|
6
|
-
import subprocess
|
|
7
|
-
|
|
8
|
-
import click
|
|
9
|
-
|
|
10
|
-
# Path to the jq binary
|
|
11
|
-
jq_path = 'jq'
|
|
12
|
-
|
|
13
|
-
@click.group()
|
|
14
|
-
def lam():
|
|
15
|
-
"""
|
|
16
|
-
lam: Laminar data transformation tool, built on jq.
|
|
17
|
-
"""
|
|
18
|
-
pass
|
|
19
|
-
|
|
20
|
-
def parse_program_file(program_file):
|
|
21
|
-
"""
|
|
22
|
-
Parses the program file to extract raw jq script.
|
|
23
|
-
Removes comments and other non-jq content.
|
|
24
|
-
"""
|
|
25
|
-
with open(program_file, 'r') as file:
|
|
26
|
-
lines = file.readlines()
|
|
27
|
-
|
|
28
|
-
return ''.join(line for line in lines if not line.strip().startswith('#'))
|
|
29
|
-
|
|
30
|
-
def run_jq(jq_script, input_data):
|
|
31
|
-
"""
|
|
32
|
-
Run the jq command with the given script and input data.
|
|
33
|
-
"""
|
|
34
|
-
process = subprocess.Popen([jq_path, jq_script], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
|
35
|
-
output, error = process.communicate(input=input_data)
|
|
36
|
-
return output, error
|
|
37
|
-
|
|
38
|
-
@lam.command()
|
|
39
|
-
@click.argument('program_file', type=click.Path(exists=True))
|
|
40
|
-
@click.argument('input', type=str)
|
|
41
|
-
def run(program_file, input):
|
|
42
|
-
"""
|
|
43
|
-
Process input (file path or raw JSON string) with a jq program file.
|
|
44
|
-
"""
|
|
45
|
-
if shutil.which("jq") is None:
|
|
46
|
-
click.echo("Error: jq is not installed. Please install jq to use this tool.", err=True)
|
|
47
|
-
raise SystemExit(1)
|
|
48
|
-
|
|
49
|
-
jq_script = parse_program_file(program_file)
|
|
50
|
-
|
|
51
|
-
# Check if input is a file path or a raw JSON string
|
|
52
|
-
if os.path.isfile(input):
|
|
53
|
-
with open(input, 'r') as file:
|
|
54
|
-
input_data = file.read()
|
|
55
|
-
else:
|
|
56
|
-
try:
|
|
57
|
-
# Validate input as JSON
|
|
58
|
-
json.loads(input)
|
|
59
|
-
input_data = input
|
|
60
|
-
except json.JSONDecodeError:
|
|
61
|
-
click.echo("Invalid JSON input.", err=True)
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
output, error = run_jq(jq_script, input_data)
|
|
65
|
-
if error:
|
|
66
|
-
click.echo(f"Error in jq processing: {error}", err=True)
|
|
67
|
-
else:
|
|
68
|
-
click.echo(output)
|
|
69
|
-
|
|
70
|
-
if __name__ == '__main__':
|
|
71
|
-
lam()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
click
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|