visivo 1.0.20__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.
Files changed (119) hide show
  1. visivo-1.0.20/PKG-INFO +37 -0
  2. visivo-1.0.20/pyproject.toml +57 -0
  3. visivo-1.0.20/visivo/__init__.py +0 -0
  4. visivo-1.0.20/visivo/__main__.py +0 -0
  5. visivo-1.0.20/visivo/command_line.py +63 -0
  6. visivo-1.0.20/visivo/commands/__init__.py +0 -0
  7. visivo-1.0.20/visivo/commands/aggregate.py +19 -0
  8. visivo-1.0.20/visivo/commands/archive.py +24 -0
  9. visivo-1.0.20/visivo/commands/archive_phase.py +36 -0
  10. visivo-1.0.20/visivo/commands/compile.py +21 -0
  11. visivo-1.0.20/visivo/commands/compile_phase.py +67 -0
  12. visivo-1.0.20/visivo/commands/deploy.py +28 -0
  13. visivo-1.0.20/visivo/commands/deploy_phase.py +75 -0
  14. visivo-1.0.20/visivo/commands/dist.py +38 -0
  15. visivo-1.0.20/visivo/commands/dist_phase.py +37 -0
  16. visivo-1.0.20/visivo/commands/init.py +16 -0
  17. visivo-1.0.20/visivo/commands/init_phase.py +166 -0
  18. visivo-1.0.20/visivo/commands/options.py +124 -0
  19. visivo-1.0.20/visivo/commands/run.py +35 -0
  20. visivo-1.0.20/visivo/commands/run_phase.py +35 -0
  21. visivo-1.0.20/visivo/commands/serve.py +28 -0
  22. visivo-1.0.20/visivo/commands/serve_phase.py +90 -0
  23. visivo-1.0.20/visivo/commands/test.py +26 -0
  24. visivo-1.0.20/visivo/commands/test_phase.py +28 -0
  25. visivo-1.0.20/visivo/commands/utils.py +53 -0
  26. visivo-1.0.20/visivo/db/utils.py +0 -0
  27. visivo-1.0.20/visivo/discovery/__init__.py +0 -0
  28. visivo-1.0.20/visivo/discovery/discover.py +95 -0
  29. visivo-1.0.20/visivo/logging/__init__.py +0 -0
  30. visivo-1.0.20/visivo/logging/logger.py +56 -0
  31. visivo-1.0.20/visivo/logging/singleton.py +37 -0
  32. visivo-1.0.20/visivo/models/__init__.py +0 -0
  33. visivo-1.0.20/visivo/models/alert.py +155 -0
  34. visivo-1.0.20/visivo/models/base/base_model.py +73 -0
  35. visivo-1.0.20/visivo/models/base/named_model.py +23 -0
  36. visivo-1.0.20/visivo/models/base/parent_model.py +169 -0
  37. visivo-1.0.20/visivo/models/base/selector_model.py +26 -0
  38. visivo-1.0.20/visivo/models/chart.py +56 -0
  39. visivo-1.0.20/visivo/models/dashboard.py +109 -0
  40. visivo-1.0.20/visivo/models/defaults.py +42 -0
  41. visivo-1.0.20/visivo/models/include.py +21 -0
  42. visivo-1.0.20/visivo/models/item.py +72 -0
  43. visivo-1.0.20/visivo/models/models/__init__.py +0 -0
  44. visivo-1.0.20/visivo/models/models/csv_script_model.py +129 -0
  45. visivo-1.0.20/visivo/models/models/fields.py +47 -0
  46. visivo-1.0.20/visivo/models/models/local_merge_model.py +98 -0
  47. visivo-1.0.20/visivo/models/models/model.py +10 -0
  48. visivo-1.0.20/visivo/models/models/sql_model.py +42 -0
  49. visivo-1.0.20/visivo/models/project.py +201 -0
  50. visivo-1.0.20/visivo/models/row.py +32 -0
  51. visivo-1.0.20/visivo/models/selector.py +47 -0
  52. visivo-1.0.20/visivo/models/table.py +122 -0
  53. visivo-1.0.20/visivo/models/table_column_definition.py +34 -0
  54. visivo-1.0.20/visivo/models/targets/fields.py +41 -0
  55. visivo-1.0.20/visivo/models/targets/mysql_target.py +37 -0
  56. visivo-1.0.20/visivo/models/targets/postgresql_target.py +37 -0
  57. visivo-1.0.20/visivo/models/targets/snowflake_target.py +74 -0
  58. visivo-1.0.20/visivo/models/targets/sqlalchemy_target.py +52 -0
  59. visivo-1.0.20/visivo/models/targets/sqlite_target.py +63 -0
  60. visivo-1.0.20/visivo/models/targets/target.py +69 -0
  61. visivo-1.0.20/visivo/models/test.py +27 -0
  62. visivo-1.0.20/visivo/models/test_run.py +56 -0
  63. visivo-1.0.20/visivo/models/tokenized_trace.py +12 -0
  64. visivo-1.0.20/visivo/models/trace.py +217 -0
  65. visivo-1.0.20/visivo/models/trace_columns.py +135 -0
  66. visivo-1.0.20/visivo/models/trace_props.py +25997 -0
  67. visivo-1.0.20/visivo/parsers/__init__.py +0 -0
  68. visivo-1.0.20/visivo/parsers/core_parser.py +69 -0
  69. visivo-1.0.20/visivo/parsers/line_validation_error.py +61 -0
  70. visivo-1.0.20/visivo/parsers/mkdocs.py +76 -0
  71. visivo-1.0.20/visivo/parsers/mkdocs_utils/markdown.py +199 -0
  72. visivo-1.0.20/visivo/parsers/mkdocs_utils/nav_configuration_generator.py +233 -0
  73. visivo-1.0.20/visivo/parsers/parser_factory.py +11 -0
  74. visivo-1.0.20/visivo/parsers/schema_generator.py +12 -0
  75. visivo-1.0.20/visivo/parsers/serializer.py +63 -0
  76. visivo-1.0.20/visivo/parsers/yaml_ordered_dict.py +59 -0
  77. visivo-1.0.20/visivo/query/__init__.py +1 -0
  78. visivo-1.0.20/visivo/query/aggregator.py +14 -0
  79. visivo-1.0.20/visivo/query/dialect.py +100 -0
  80. visivo-1.0.20/visivo/query/jobs/__init__.py +1 -0
  81. visivo-1.0.20/visivo/query/jobs/job.py +90 -0
  82. visivo-1.0.20/visivo/query/jobs/run_csv_script_job.py +59 -0
  83. visivo-1.0.20/visivo/query/jobs/run_local_merge_job.py +65 -0
  84. visivo-1.0.20/visivo/query/jobs/run_trace_job.py +86 -0
  85. visivo-1.0.20/visivo/query/query_string_factory.py +17 -0
  86. visivo-1.0.20/visivo/query/query_writer.py +21 -0
  87. visivo-1.0.20/visivo/query/runner.py +138 -0
  88. visivo-1.0.20/visivo/query/statement_classifier.py +29 -0
  89. visivo-1.0.20/visivo/query/target_job_tracker.py +128 -0
  90. visivo-1.0.20/visivo/query/trace_tokenizer.py +136 -0
  91. visivo-1.0.20/visivo/src/plotly_config_meta.json +1 -0
  92. visivo-1.0.20/visivo/templates/__init__.py +0 -0
  93. visivo-1.0.20/visivo/templates/charts/dashboard.html +19 -0
  94. visivo-1.0.20/visivo/templates/charts/default_chart.txt +50 -0
  95. visivo-1.0.20/visivo/templates/charts/default_trace.txt +20 -0
  96. visivo-1.0.20/visivo/templates/queries/default_trace.sql +54 -0
  97. visivo-1.0.20/visivo/templates/render_yaml.py +135 -0
  98. visivo-1.0.20/visivo/testing/__init__.py +0 -0
  99. visivo-1.0.20/visivo/testing/runner.py +79 -0
  100. visivo-1.0.20/visivo/utils.py +108 -0
  101. visivo-1.0.20/visivo/viewer/about.txt +6 -0
  102. visivo-1.0.20/visivo/viewer/android-chrome-192x192.png +0 -0
  103. visivo-1.0.20/visivo/viewer/android-chrome-512x512.png +0 -0
  104. visivo-1.0.20/visivo/viewer/apple-touch-icon.png +0 -0
  105. visivo-1.0.20/visivo/viewer/asset-manifest.json +15 -0
  106. visivo-1.0.20/visivo/viewer/favicon-16x16.png +0 -0
  107. visivo-1.0.20/visivo/viewer/favicon-32x32.png +0 -0
  108. visivo-1.0.20/visivo/viewer/favicon.ico +0 -0
  109. visivo-1.0.20/visivo/viewer/index.html +1 -0
  110. visivo-1.0.20/visivo/viewer/manifest.json +25 -0
  111. visivo-1.0.20/visivo/viewer/robots.txt +3 -0
  112. visivo-1.0.20/visivo/viewer/site.webmanifest +1 -0
  113. visivo-1.0.20/visivo/viewer/static/css/main.5285427e.css +4 -0
  114. visivo-1.0.20/visivo/viewer/static/css/main.5285427e.css.map +1 -0
  115. visivo-1.0.20/visivo/viewer/static/js/488.72e7b0fa.chunk.js +2 -0
  116. visivo-1.0.20/visivo/viewer/static/js/488.72e7b0fa.chunk.js.map +1 -0
  117. visivo-1.0.20/visivo/viewer/static/js/main.5aa6f1f8.js +3 -0
  118. visivo-1.0.20/visivo/viewer/static/js/main.5aa6f1f8.js.LICENSE.txt +192 -0
  119. visivo-1.0.20/visivo/viewer/static/js/main.5aa6f1f8.js.map +1 -0
visivo-1.0.20/PKG-INFO ADDED
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.1
2
+ Name: visivo
3
+ Version: 1.0.20
4
+ Summary:
5
+ Author: Visivo People
6
+ Requires-Python: >=3.10,<3.13
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Requires-Dist: Flask (>=2.2.3,<3.0.0)
12
+ Requires-Dist: Jinja2 (>=3.1.2,<4.0.0)
13
+ Requires-Dist: PyYAML (>=6.0,<7.0)
14
+ Requires-Dist: assertpy (>=1.1,<2.0)
15
+ Requires-Dist: click (>=8.1.3,<9.0.0)
16
+ Requires-Dist: deepmerge (>=1.1.0,<2.0.0)
17
+ Requires-Dist: gitpython (>=3.1.32,<4.0.0)
18
+ Requires-Dist: halo (>=0.0.31,<0.0.32)
19
+ Requires-Dist: livereload (>=2.6.3,<3.0.0)
20
+ Requires-Dist: matplotlib (>=3.7.1,<4.0.0)
21
+ Requires-Dist: mkdocs-autolinks-plugin (>=0.7.1,<0.8.0)
22
+ Requires-Dist: networkx (>=3.1,<4.0)
23
+ Requires-Dist: numpy (==1.26.0)
24
+ Requires-Dist: pandas (>=1.5.3,<2.0.0)
25
+ Requires-Dist: psycopg2-binary (>=2.9.5,<3.0.0)
26
+ Requires-Dist: pydantic (>=2.5.0)
27
+ Requires-Dist: pymysql (>=1.1.0,<2.0.0)
28
+ Requires-Dist: pytest-mock (>=3.14.0,<4.0.0)
29
+ Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
30
+ Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
31
+ Requires-Dist: requests (>=2.28.2,<3.0.0)
32
+ Requires-Dist: snowflake-connector-python (>=3.7.0,<4.0.0)
33
+ Requires-Dist: sql-formatter (>=0.6.2,<0.7.0)
34
+ Requires-Dist: sqlalchemy (>=2.0.8,<3.0.0)
35
+ Requires-Dist: termcolor (>=2.4.0,<3.0.0)
36
+ Requires-Dist: tornado (<6.3)
37
+ Project-URL: Documentation, https://docs.visivo.io/
@@ -0,0 +1,57 @@
1
+ [tool.poetry]
2
+ name = "visivo"
3
+ version = "1.0.20"
4
+ description = ""
5
+ authors = ["Visivo People"]
6
+ documentation = "https://docs.visivo.io/"
7
+
8
+ [tool.poetry.scripts]
9
+ visivo = "visivo.command_line:safe_visivo"
10
+
11
+ [tool.poetry.dependencies]
12
+ python = " >=3.10,<3.13"
13
+ pydantic = ">=2.5.0"
14
+ requests = "^2.28.2"
15
+ Jinja2 = "^3.1.2"
16
+ PyYAML = "^6.0"
17
+ sql-formatter = "^0.6.2"
18
+ click = "^8.1.3"
19
+ pandas = "^1.5.3"
20
+ Flask = "^2.2.3"
21
+ livereload = "^2.6.3"
22
+ tornado = "< 6.3"
23
+ numpy = "=1.26.0"
24
+ psycopg2-binary = "^2.9.5"
25
+ sqlalchemy = "^2.0.8"
26
+ snowflake-connector-python = "^3.7.0"
27
+ python-dotenv = "^1.0.0"
28
+ networkx = "^3.1"
29
+ matplotlib = "^3.7.1"
30
+ deepmerge = "^1.1.0"
31
+ gitpython = "^3.1.32"
32
+ halo = "^0.0.31"
33
+ mkdocs-autolinks-plugin = "^0.7.1"
34
+ termcolor = "^2.4.0"
35
+ python-dateutil = "^2.9.0.post0"
36
+ pytest-mock = "^3.14.0"
37
+ pymysql = "^1.1.0"
38
+ assertpy = "^1.1"
39
+
40
+ [tool.poetry.group.dev.dependencies]
41
+ pytest = "^7.2.1"
42
+ factory-boy = "^3.2.1"
43
+ mkdocs = "^1.4.2"
44
+ mkdocs-click = "^0.8.0"
45
+ mkdocs-encryptcontent-plugin = "^2.4.5"
46
+ mkdocs-material = "^9.1.3"
47
+ mkdocs-spellcheck = {extras = ["all"], version = "^1.0.0"}
48
+ symspellpy = "^6.7.7"
49
+ codespell = "^2.2.4"
50
+ mkdocs-macros-plugin = "^0.7.0"
51
+ setuptools = "^67.7.2"
52
+ pytest-env = "^1.0.1"
53
+ requests-mock = "^1.11.0"
54
+
55
+ [build-system]
56
+ requires = ["poetry-core>=1.0.0"]
57
+ build-backend = "poetry.core.masonry.api"
File without changes
File without changes
@@ -0,0 +1,63 @@
1
+ import click
2
+ import os
3
+ import importlib
4
+ from dotenv import load_dotenv
5
+ from pydantic import ValidationError
6
+ from visivo.logging.logger import Logger, TypeEnum
7
+ from visivo.parsers.line_validation_error import LineValidationError
8
+
9
+ from .commands.deploy import deploy
10
+ from .commands.serve import serve
11
+ from .commands.run import run
12
+ from .commands.dist import dist
13
+ from .commands.compile import compile
14
+ from .commands.init import init
15
+ from .commands.test import test
16
+ from .commands.aggregate import aggregate
17
+ from .commands.archive import archive
18
+
19
+
20
+ @click.group()
21
+ @click.option("-e", "--env-file", default=".env")
22
+ @click.version_option(version=importlib.metadata.version("visivo"))
23
+ def visivo(env_file):
24
+ Logger.instance().set_type(TypeEnum.spinner)
25
+ load_env(env_file)
26
+
27
+
28
+ visivo.add_command(init)
29
+ visivo.add_command(compile)
30
+ visivo.add_command(run)
31
+ visivo.add_command(serve)
32
+ visivo.add_command(deploy)
33
+ visivo.add_command(dist)
34
+ visivo.add_command(test)
35
+ visivo.add_command(aggregate)
36
+ visivo.add_command(archive)
37
+
38
+
39
+ def load_env(env_file):
40
+ if os.path.isfile(env_file):
41
+ Logger.instance().debug(f"Loading env file: {env_file}")
42
+ load_dotenv(env_file)
43
+
44
+
45
+ def safe_visivo():
46
+ try:
47
+ visivo(standalone_mode=False)
48
+ except (ValidationError, LineValidationError) as e:
49
+ Logger.instance().error(str(e))
50
+ exit(1)
51
+ except Exception as e:
52
+ if "STACKTRACE" in os.environ and os.environ["STACKTRACE"] == "true":
53
+ raise e
54
+ Logger.instance().error("An unexpected error has occurred")
55
+ Logger.instance().error(str(e))
56
+ Logger.instance().error(
57
+ "To print more error information add the 'STACKTRACE=true' environment variable."
58
+ )
59
+ exit(1)
60
+
61
+
62
+ if __name__ == "__main__":
63
+ safe_visivo()
File without changes
@@ -0,0 +1,19 @@
1
+ import click
2
+ from visivo.query.aggregator import Aggregator
3
+ from .options import output_dir
4
+
5
+
6
+ @click.command()
7
+ @output_dir
8
+ @click.option(
9
+ "-j", "--json-file", help="The file with the raw json results from the query"
10
+ )
11
+ def aggregate(output_dir, json_file):
12
+ from visivo.logging.logger import Logger
13
+
14
+ Logger.instance().debug("Aggregating")
15
+
16
+ from visivo.query.runner import Runner
17
+
18
+ Aggregator.aggregate(trace_dir=output_dir, json_file=json_file)
19
+ Logger.instance().success("Done")
@@ -0,0 +1,24 @@
1
+ import click
2
+ from .options import user_dir, stage, host
3
+
4
+
5
+ @click.command()
6
+ @stage
7
+ @host
8
+ @user_dir
9
+ def archive(stage, host, user_dir):
10
+ """
11
+ Archives a stage. You must specify a stage when deploying a project.
12
+ """
13
+ from visivo.logging.logger import Logger
14
+
15
+ Logger.instance().debug("Archiving")
16
+
17
+ from visivo.commands.archive_phase import archive_phase
18
+
19
+ archive_phase(
20
+ user_dir=user_dir,
21
+ stage=stage,
22
+ host=host,
23
+ )
24
+ Logger.instance().success("Done")
@@ -0,0 +1,36 @@
1
+ from visivo.logging.logger import Logger
2
+ import click
3
+ import requests
4
+ import json
5
+ from visivo.commands.utils import get_profile_file, get_profile_token
6
+
7
+
8
+ def archive_phase(stage, host, user_dir):
9
+ profile_token = get_profile_token(get_profile_file(home_directory=user_dir))
10
+
11
+ json_headers = {
12
+ "content-type": "application/json",
13
+ "Authorization": f"Api-Key {profile_token}",
14
+ }
15
+
16
+ url = f"{host}/api/stages/?name={stage}"
17
+ response = requests.get(url, headers=json_headers)
18
+ if response.status_code == 401:
19
+ raise click.ClickException(f"Token not authorized for host: {host}")
20
+ if response.status_code == 404:
21
+ raise click.ClickException(f"404 error raised. Does your user have an account?")
22
+
23
+ if len(response.json()) == 0:
24
+ Logger.instance().debug(f"No stages with name {stage} found")
25
+ return
26
+
27
+ id = response.json()[0]["id"]
28
+ body = {
29
+ "archived": True,
30
+ }
31
+ url = f"{host}/api/stages/{id}/"
32
+ response = requests.put(url, data=json.dumps(body), headers=json_headers)
33
+ if response.status_code == 200:
34
+ Logger.instance().debug("Stage archived")
35
+ else:
36
+ raise click.ClickException(f"There was an unexpected error: {response.content}")
@@ -0,0 +1,21 @@
1
+ import click
2
+ from .options import output_dir, working_dir, target, name_filter
3
+
4
+
5
+ @click.command()
6
+ @target
7
+ @working_dir
8
+ @output_dir
9
+ @name_filter
10
+ def compile(working_dir, output_dir, target, name_filter):
11
+ """
12
+ Parses the files in your working directory, extracting visivo configurations and then using those configurations to build the trace queries and a project.json file in your target directory. Queries are not run on compile, just written.
13
+ """
14
+ from visivo.logging.logger import Logger
15
+
16
+ Logger.instance().debug("Compiling")
17
+
18
+ from visivo.commands.compile_phase import compile_phase
19
+
20
+ compile_phase(default_target=target, working_dir=working_dir, output_dir=output_dir, name_filter=name_filter)
21
+ Logger.instance().success("Done")
@@ -0,0 +1,67 @@
1
+ import click
2
+ import os
3
+ import yaml
4
+ from visivo.discovery.discover import Discover
5
+ from visivo.models.defaults import Defaults
6
+ from visivo.models.models.csv_script_model import CsvScriptModel
7
+ from visivo.models.targets.target import Target
8
+ from visivo.models.models.model import Model
9
+ from visivo.models.base.parent_model import ParentModel
10
+ from visivo.parsers.parser_factory import ParserFactory
11
+ from visivo.parsers.serializer import Serializer
12
+ from visivo.query.query_string_factory import QueryStringFactory
13
+ from visivo.query.trace_tokenizer import TraceTokenizer
14
+ from visivo.query.query_writer import QueryWriter
15
+ from visivo.logging.logger import Logger
16
+
17
+
18
+ def compile_phase(
19
+ default_target: str, working_dir: str, output_dir: str, name_filter: str = None
20
+ ):
21
+ Logger.instance().debug("Compiling project")
22
+ discover = Discover(working_directory=working_dir)
23
+ parser = ParserFactory().build(
24
+ project_file=discover.project_file, files=discover.files
25
+ )
26
+ project = None
27
+ try:
28
+ project = parser.parse()
29
+ if not project.defaults:
30
+ project.defaults = Defaults()
31
+ if default_target:
32
+ project.defaults.target_name = default_target
33
+ except yaml.YAMLError as e:
34
+ message = "\n"
35
+ if hasattr(e, "problem_mark"):
36
+ mark = e.problem_mark
37
+ message = f"\n Error position: line:{mark.line+1} column:{mark.column+1}\n"
38
+ raise click.ClickException(
39
+ f"There was an error parsing the yml file(s):{message} {e}"
40
+ )
41
+
42
+ os.makedirs(output_dir, exist_ok=True)
43
+ with open(f"{output_dir}/project.json", "w") as fp:
44
+ serializer = Serializer(project=project)
45
+ fp.write(serializer.dereference().model_dump_json(exclude_none=True))
46
+
47
+ dag = project.dag()
48
+ for trace in project.filter_traces(name_filter=name_filter):
49
+ model = ParentModel.all_descendants_of_type(
50
+ type=Model, dag=dag, from_node=trace
51
+ )[0]
52
+ if isinstance(model, CsvScriptModel):
53
+ target = model.get_sqlite_target(output_dir=output_dir)
54
+ else:
55
+ target = ParentModel.all_descendants_of_type(
56
+ type=Target, dag=dag, from_node=model
57
+ )[0]
58
+ tokenized_trace = TraceTokenizer(
59
+ trace=trace, model=model, target=target
60
+ ).tokenize()
61
+ query_string = QueryStringFactory(tokenized_trace=tokenized_trace).build()
62
+ QueryWriter(
63
+ trace=trace, query_string=query_string, output_dir=output_dir
64
+ ).write()
65
+
66
+ Logger.instance().debug("Project compiled")
67
+ return project
@@ -0,0 +1,28 @@
1
+ import click
2
+ from visivo.commands.options import output_dir, working_dir, user_dir, stage, host
3
+
4
+
5
+ @click.command()
6
+ @working_dir
7
+ @output_dir
8
+ @stage
9
+ @host
10
+ @user_dir
11
+ def deploy(working_dir, user_dir, output_dir, stage, host):
12
+ """
13
+ Sends the current version of your project, traces & data to app.visivo.io where it can be viewed by other users on your account. You must specify a stage when deploying a project. The stage allows multiple versions of your project to exist remotely. This is very useful for setting up different dev, CI and production environments.
14
+ """
15
+ from visivo.logging.logger import Logger
16
+
17
+ Logger.instance().debug("Deploying")
18
+
19
+ from visivo.commands.deploy_phase import deploy_phase
20
+
21
+ url = deploy_phase(
22
+ user_dir=user_dir,
23
+ working_dir=working_dir,
24
+ output_dir=output_dir,
25
+ stage=stage,
26
+ host=host,
27
+ )
28
+ Logger.instance().success(f"Deployed to: '{host}{url}'")
@@ -0,0 +1,75 @@
1
+ import click
2
+ import requests
3
+ import json
4
+ from visivo.commands.utils import get_profile_file, get_profile_token
5
+ from visivo.discovery.discover import Discover
6
+ from visivo.logging.logger import Logger
7
+ from visivo.parsers.serializer import Serializer
8
+ from visivo.parsers.parser_factory import ParserFactory
9
+
10
+
11
+ def deploy_phase(working_dir, user_dir, output_dir, stage, host):
12
+ """
13
+ Sends the current version of your project, traces & data to app.visivo.io where it can be viewed by other users on your account. You must specify a stage when deploying a project. The stage allows multiple versions of your project to exist remotely. This is very useful for setting up different dev, CI and production environments.
14
+ """
15
+ profile_token = get_profile_token(get_profile_file(home_directory=user_dir))
16
+
17
+ discover = Discover(working_directory=working_dir, home_directory=user_dir)
18
+ parser = ParserFactory().build(
19
+ project_file=discover.project_file, files=discover.files
20
+ )
21
+ project = parser.parse()
22
+ serializer = Serializer(project=project)
23
+ project_json = json.loads(
24
+ serializer.dereference().model_dump_json(exclude_none=True)
25
+ )
26
+
27
+ body = {
28
+ "project_json": project_json,
29
+ "name": project_json["name"],
30
+ "cli_version": project_json["cli_version"],
31
+ "stage": stage,
32
+ }
33
+ json_headers = {
34
+ "content-type": "application/json",
35
+ "Authorization": f"Api-Key {profile_token}",
36
+ }
37
+ form_headers = {
38
+ "Authorization": f"Api-Key {profile_token}",
39
+ }
40
+
41
+ url = f"{host}/api/projects/"
42
+ response = requests.post(url, data=json.dumps(body), headers=json_headers)
43
+ if response.status_code == 401:
44
+ raise click.ClickException(f"Token not authorized for host: {host}")
45
+ if response.status_code == 404:
46
+ raise click.ClickException(f"404 error raised. Does your user have an account?")
47
+ if response.status_code == 201:
48
+ Logger.instance().debug("Project uploaded")
49
+ project_data = response.json()
50
+ project_id = project_data["id"]
51
+ project_url = project_data["url"]
52
+
53
+ for trace in project.trace_objs:
54
+ url = f"{host}/api/files/"
55
+
56
+ data_file = f"{output_dir}/{trace.name}/data.json"
57
+ files = {"file": open(data_file, "rb")}
58
+ response = requests.post(url, files=files, data={}, headers=form_headers)
59
+ if response.status_code != 201:
60
+ raise click.ClickException(f"Trace '{trace.name}' data not uploaded")
61
+ Logger.instance().debug(f"Trace '{trace.name}' data uploaded")
62
+ url = f"{host}/api/traces/"
63
+ body = {
64
+ "name": trace.name,
65
+ "project_id": project_id,
66
+ "data_file_id": response.json()["id"],
67
+ }
68
+ response = requests.post(url, data=json.dumps(body), headers=json_headers)
69
+ if response.status_code != 201:
70
+ Logger.instance().debug(response.json())
71
+ raise click.ClickException(f"Trace '{trace.name}' not created")
72
+ Logger.instance().debug(f"Trace '{trace.name}' created")
73
+ return project_url
74
+ else:
75
+ raise click.ClickException(f"There was an unexpected error: {response.content}")
@@ -0,0 +1,38 @@
1
+ import click
2
+ from visivo.commands.options import (
3
+ dist_dir,
4
+ name_filter,
5
+ output_dir,
6
+ target,
7
+ threads,
8
+ working_dir,
9
+ )
10
+
11
+
12
+ @click.command()
13
+ @target
14
+ @working_dir
15
+ @output_dir
16
+ @dist_dir
17
+ @name_filter
18
+ @threads
19
+ def dist(output_dir, working_dir, dist_dir, target, name_filter, threads):
20
+ """
21
+ Creates a distributable version of this dashboard and stores it in a 'dist' folder.
22
+ This folder can be statically deployed to any web server, hosting service, or bucket storage.
23
+ """
24
+ from visivo.logging.logger import Logger
25
+
26
+ Logger.instance().debug("Creating Dist")
27
+
28
+ from visivo.commands.dist_phase import dist_phase
29
+
30
+ dist_phase(
31
+ output_dir=output_dir,
32
+ working_dir=working_dir,
33
+ dist_dir=dist_dir,
34
+ default_target=target,
35
+ name_filter=name_filter,
36
+ threads=threads,
37
+ )
38
+ Logger.instance().success(f"Created dist folder")
@@ -0,0 +1,37 @@
1
+ import pkg_resources
2
+
3
+ VIEWER_PATH = pkg_resources.resource_filename("visivo", "viewer/")
4
+
5
+
6
+ def dist_phase(output_dir, working_dir, dist_dir, default_target, name_filter, threads):
7
+ from visivo.commands.run_phase import run_phase
8
+ import os
9
+ import json
10
+ import shutil
11
+ from glob import glob
12
+
13
+ run_phase(
14
+ output_dir=output_dir,
15
+ working_dir=working_dir,
16
+ default_target=default_target,
17
+ name_filter=name_filter,
18
+ threads=threads,
19
+ )
20
+
21
+ os.makedirs(f"{dist_dir}/data", exist_ok=True)
22
+ with open(f"{output_dir}/project.json", "r") as f:
23
+ project_json = json.load(f)
24
+ with open(f"{dist_dir}/data/project.json", "w") as f:
25
+ f.write(json.dumps({"project_json": project_json}))
26
+
27
+ trace_dirs = glob(f"{output_dir}/*/", recursive=True)
28
+ for trace_dir in trace_dirs:
29
+ trace_name = os.path.basename(os.path.normpath(trace_dir))
30
+ if os.path.exists(f"{output_dir}/{trace_name}/data.json"):
31
+ os.makedirs(f"{dist_dir}/data/{trace_name}", exist_ok=True)
32
+ shutil.copyfile(
33
+ f"{output_dir}/{trace_name}/data.json",
34
+ f"{dist_dir}/data/{trace_name}/data.json",
35
+ )
36
+
37
+ shutil.copytree(VIEWER_PATH, dist_dir, dirs_exist_ok=True)
@@ -0,0 +1,16 @@
1
+ import click
2
+
3
+
4
+ @click.command()
5
+ def init():
6
+ """
7
+ Enables a quick set up by writing your target & api credentials to an env file.
8
+ """
9
+ from visivo.logging.logger import Logger
10
+
11
+ Logger.instance().debug("Initializing")
12
+
13
+ from visivo.commands.init_phase import init_phase
14
+
15
+ init_phase()
16
+ Logger.instance().success("Done")