lam-cli 0.0.2__py3-none-any.whl → 0.0.3__py3-none-any.whl

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/lam.py CHANGED
@@ -1,71 +1,145 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
3
  import json
4
+ import logging
4
5
  import os
5
6
  import shutil
7
+ import socket
6
8
  import subprocess
9
+ import threading
10
+ from datetime import datetime
11
+ from queue import Queue
7
12
 
8
13
  import click
14
+ from posthog import Posthog
15
+
16
+ posthog = Posthog(project_api_key='phc_wfeHFG0p5yZIdBpjVYy00o5x1HbEpggdMzIuFYgNPSK', host='https://app.posthog.com')
9
17
 
10
- # Path to the jq binary
11
18
  jq_path = 'jq'
19
+ event_queue = Queue()
12
20
 
13
- @click.group()
14
- def lam():
15
- """
16
- lam: Laminar data transformation tool, built on jq.
17
- """
18
- pass
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()
19
38
 
20
39
  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
- """
40
+ logging.info(f"Parsing program file: {program_file}")
25
41
  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('#'))
42
+ return ''.join(line for line in file if not line.strip().startswith('#'))
29
43
 
30
44
  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)
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)
35
47
  output, error = process.communicate(input=input_data)
48
+ if error:
49
+ logging.error(f"Error running jq: {error}")
36
50
  return output, error
37
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
+
38
90
  @lam.command()
39
91
  @click.argument('program_file', type=click.Path(exists=True))
40
92
  @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)
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
48
120
 
49
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)
50
124
 
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)
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)
65
132
  if error:
66
- click.echo(f"Error in jq processing: {error}", err=True)
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)
67
136
  else:
68
- click.echo(output)
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")
69
142
 
70
143
  if __name__ == '__main__':
144
+ threading.Thread(target=event_logger, daemon=True).start()
71
145
  lam()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lam-cli
3
- Version: 0.0.2
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.
@@ -0,0 +1,8 @@
1
+ lam/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ lam/lam.py,sha256=xpOPerS3EMqh2Ugzy-kfqXlXJctoPY6aszdViTJmrS4,6645
3
+ lam_cli-0.0.3.dist-info/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
4
+ lam_cli-0.0.3.dist-info/METADATA,sha256=dU8aX-e4nelxjOl70h7_ZkSUP3_DJDvLMpccsjThB6s,374
5
+ lam_cli-0.0.3.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
6
+ lam_cli-0.0.3.dist-info/entry_points.txt,sha256=ph7QV6H2VWqf9fU5rtoAgEabDgZ4f85ZImdLXeBmdfA,36
7
+ lam_cli-0.0.3.dist-info/top_level.txt,sha256=WyM7-Ig60qQH9meqS293pEd83jrMtbvGJM8ALZOQCtA,4
8
+ lam_cli-0.0.3.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- lam/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- lam/lam.py,sha256=1MOeqSf8Z6Xo8EPkufRLyT7u1GNrXLU5CGnUf4dbSzI,1901
3
- lam_cli-0.0.2.dist-info/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
4
- lam_cli-0.0.2.dist-info/METADATA,sha256=WmkdzdzkPOSum7UpzAidXuliK_VhUwMy0L7VeLLRreo,351
5
- lam_cli-0.0.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
6
- lam_cli-0.0.2.dist-info/entry_points.txt,sha256=ph7QV6H2VWqf9fU5rtoAgEabDgZ4f85ZImdLXeBmdfA,36
7
- lam_cli-0.0.2.dist-info/top_level.txt,sha256=WyM7-Ig60qQH9meqS293pEd83jrMtbvGJM8ALZOQCtA,4
8
- lam_cli-0.0.2.dist-info/RECORD,,