jsongrapher 3.7__py3-none-any.whl → 3.9__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.
- JSONGrapher/JSONRecordCreator.py +84 -20
- {jsongrapher-3.7.dist-info → jsongrapher-3.9.dist-info}/METADATA +3 -3
- {jsongrapher-3.7.dist-info → jsongrapher-3.9.dist-info}/RECORD +8 -8
- {jsongrapher-3.7.data → jsongrapher-3.9.data}/data/LICENSE +0 -0
- {jsongrapher-3.7.data → jsongrapher-3.9.data}/data/README.md +0 -0
- {jsongrapher-3.7.dist-info → jsongrapher-3.9.dist-info}/LICENSE +0 -0
- {jsongrapher-3.7.dist-info → jsongrapher-3.9.dist-info}/WHEEL +0 -0
- {jsongrapher-3.7.dist-info → jsongrapher-3.9.dist-info}/top_level.txt +0 -0
JSONGrapher/JSONRecordCreator.py
CHANGED
@@ -8,6 +8,7 @@ import JSONGrapher.styles.trace_styles_collection_library
|
|
8
8
|
global_records_list = [] #This list holds onto records as they are added. Index 0 is the merged record. Each other index corresponds to record number (like 1 is first record, 2 is second record, etc)
|
9
9
|
|
10
10
|
|
11
|
+
|
11
12
|
#This is a JSONGrapher specific function
|
12
13
|
#That takes filenames and adds new JSONGrapher records to a global_records_list
|
13
14
|
#If the all_selected_file_paths and newest_file_name_and_path are [] and [], that means to clear the global_records_list.
|
@@ -830,6 +831,7 @@ class JSONGrapherRecord:
|
|
830
831
|
def evaluate_eqution_of_data_series_by_index(self, series_index, equation_dict = None, verbose=False):
|
831
832
|
if equation_dict != None:
|
832
833
|
self.fig_dict["data"][series_index]["equation"] = equation_dict
|
834
|
+
data_series_dict = self.fig_dict["data"][series_index]
|
833
835
|
self.fig_dict = evaluate_equation_for_data_series_by_index(data_series_index=data_series_dict, verbose=verbose) #implied return.
|
834
836
|
return data_series_dict #Extra regular return
|
835
837
|
|
@@ -841,11 +843,11 @@ class JSONGrapherRecord:
|
|
841
843
|
return self.fig_dict
|
842
844
|
#The update_and_validate function will clean for plotly.
|
843
845
|
#TODO: the internal recommending "print_to_inspect" function should, by default, exclude printing the full dictionaries of the layout_style and the trace_collection_style.
|
844
|
-
def print_to_inspect(self, update_and_validate=True, validate=True, remove_remaining_hints=False):
|
846
|
+
def print_to_inspect(self, update_and_validate=True, validate=True, clean_for_plotly = True, remove_remaining_hints=False):
|
845
847
|
if remove_remaining_hints == True:
|
846
848
|
self.remove_hints()
|
847
849
|
if update_and_validate == True: #this will do some automatic 'corrections' during the validation.
|
848
|
-
self.update_and_validate_JSONGrapher_record()
|
850
|
+
self.update_and_validate_JSONGrapher_record(clean_for_plotly=clean_for_plotly)
|
849
851
|
elif validate: #this will validate without doing automatic updates.
|
850
852
|
self.validate_JSONGrapher_record()
|
851
853
|
print(json.dumps(self.fig_dict, indent=4))
|
@@ -895,9 +897,7 @@ class JSONGrapherRecord:
|
|
895
897
|
#now, add the scaled data objects to the original one.
|
896
898
|
#This is fairly easy using a list extend.
|
897
899
|
self.fig_dict["data"].extend(scaled_fig_dict["data"])
|
898
|
-
|
899
|
-
|
900
|
-
|
900
|
+
|
901
901
|
def import_from_dict(self, fig_dict):
|
902
902
|
self.fig_dict = fig_dict
|
903
903
|
|
@@ -1046,7 +1046,7 @@ class JSONGrapherRecord:
|
|
1046
1046
|
elif validate: #this will validate without doing automatic updates.
|
1047
1047
|
self.validate_JSONGrapher_record()
|
1048
1048
|
|
1049
|
-
#
|
1049
|
+
# filename with path to save the JSON file.
|
1050
1050
|
if len(filename) > 0: #this means we will be writing to file.
|
1051
1051
|
# Check if the filename has an extension and append `.json` if not
|
1052
1052
|
if '.json' not in filename.lower():
|
@@ -1056,6 +1056,18 @@ class JSONGrapherRecord:
|
|
1056
1056
|
json.dump(self.fig_dict, f, indent=4)
|
1057
1057
|
return self.fig_dict
|
1058
1058
|
|
1059
|
+
def export_plotly_json(self, filename, plot_style = None, update_and_validate=True, simulate_all_series=True, evaluate_all_equations=True,adjust_implicit_data_ranges=True):
|
1060
|
+
fig = self.get_plotly_fig(plot_style=plot_style, update_and_validate=update_and_validate, simulate_all_series=simulate_all_series, evaluate_all_equations=evaluate_all_equations, adjust_implicit_data_ranges=adjust_implicit_data_ranges)
|
1061
|
+
plotly_json_string = fig.to_plotly_json()
|
1062
|
+
if len(filename) > 0: #this means we will be writing to file.
|
1063
|
+
# Check if the filename has an extension and append `.json` if not
|
1064
|
+
if '.json' not in filename.lower():
|
1065
|
+
filename += ".json"
|
1066
|
+
#Write to file using UTF-8 encoding.
|
1067
|
+
with open(filename, 'w', encoding='utf-8') as f:
|
1068
|
+
json.dump(plotly_json_string, f, indent=4)
|
1069
|
+
return plotly_json_string
|
1070
|
+
|
1059
1071
|
#simulate all series will simulate any series as needed.
|
1060
1072
|
def get_plotly_fig(self, plot_style=None, update_and_validate=True, simulate_all_series=True, evaluate_all_equations=True, adjust_implicit_data_ranges=True):
|
1061
1073
|
"""
|
@@ -1091,8 +1103,8 @@ class JSONGrapherRecord:
|
|
1091
1103
|
self.apply_plot_style(plot_style=plot_style)
|
1092
1104
|
#Now we clean out the fields and make a plotly object.
|
1093
1105
|
if update_and_validate == True: #this will do some automatic 'corrections' during the validation.
|
1094
|
-
self.update_and_validate_JSONGrapher_record() #
|
1095
|
-
self.fig_dict = clean_json_fig_dict(self.fig_dict, fields_to_update=['simulate', 'custom_units_chevrons', 'equation', 'trace_style', '3d_axes', 'bubble'])
|
1106
|
+
self.update_and_validate_JSONGrapher_record(clean_for_plotly=False) #We use the False argument here because the cleaning will be on the next line with beyond default arguments.
|
1107
|
+
self.fig_dict = clean_json_fig_dict(self.fig_dict, fields_to_update=['simulate', 'custom_units_chevrons', 'equation', 'trace_style', '3d_axes', 'bubble', 'superscripts'])
|
1096
1108
|
fig = pio.from_json(json.dumps(self.fig_dict))
|
1097
1109
|
#restore the original fig_dict.
|
1098
1110
|
self.fig_dict = original_fig_dict
|
@@ -1389,8 +1401,8 @@ class JSONGrapherRecord:
|
|
1389
1401
|
#Make some pointers to external functions, for convenience, so people can use syntax like record.function_name() if desired.
|
1390
1402
|
def validate_JSONGrapher_record(self):
|
1391
1403
|
validate_JSONGrapher_record(self)
|
1392
|
-
def update_and_validate_JSONGrapher_record(self):
|
1393
|
-
update_and_validate_JSONGrapher_record(self)
|
1404
|
+
def update_and_validate_JSONGrapher_record(self, clean_for_plotly=True):
|
1405
|
+
update_and_validate_JSONGrapher_record(self, clean_for_plotly=clean_for_plotly)
|
1394
1406
|
|
1395
1407
|
|
1396
1408
|
# helper function to validate x axis and y axis labels.
|
@@ -2927,24 +2939,61 @@ def get_fig_dict_ranges(fig_dict, skip_equations=False, skip_simulations=False):
|
|
2927
2939
|
|
2928
2940
|
### Start section of code with functions for cleaning fig_dicts for plotly compatibility ###
|
2929
2941
|
|
2930
|
-
def update_title_field(
|
2942
|
+
def update_title_field(fig_dict_or_subdict, depth=1, max_depth=10):
|
2931
2943
|
""" This function is intended to make JSONGrapher .json files compatible with the newer plotly recommended title field formatting
|
2932
2944
|
which is necessary to do things like change the font, and also necessary for being able to convert a JSONGrapher json_dict to python plotly figure objects.
|
2933
2945
|
Recursively checks for 'title' fields and converts them to dictionary format. """
|
2934
|
-
if depth > max_depth or not isinstance(
|
2935
|
-
return
|
2946
|
+
if depth > max_depth or not isinstance(fig_dict_or_subdict, dict):
|
2947
|
+
return fig_dict_or_subdict
|
2936
2948
|
|
2937
|
-
for key, value in
|
2938
|
-
if key == "title" and isinstance(value, str):
|
2939
|
-
|
2949
|
+
for key, value in fig_dict_or_subdict.items():
|
2950
|
+
if key == "title" and isinstance(value, str): #This is for axes labels.
|
2951
|
+
fig_dict_or_subdict[key] = {"text": value}
|
2940
2952
|
elif isinstance(value, dict): # Nested dictionary
|
2941
|
-
|
2953
|
+
fig_dict_or_subdict[key] = update_title_field(value, depth + 1, max_depth)
|
2942
2954
|
elif isinstance(value, list): # Lists can contain nested dictionaries
|
2943
|
-
|
2944
|
-
return
|
2955
|
+
fig_dict_or_subdict[key] = [update_title_field(item, depth + 1, max_depth) if isinstance(item, dict) else item for item in value]
|
2956
|
+
return fig_dict_or_subdict
|
2957
|
+
|
2945
2958
|
|
2946
2959
|
|
2947
2960
|
|
2961
|
+
def update_superscripts_strings(fig_dict_or_subdict, depth=1, max_depth=10):
|
2962
|
+
""" This function is intended to make JSONGrapher .json files compatible with the newer plotly recommended title field formatting
|
2963
|
+
which is necessary to do things like change the font, and also necessary for being able to convert a JSONGrapher json_dict to python plotly figure objects.
|
2964
|
+
Recursively checks for 'title' fields and converts them to dictionary format. """
|
2965
|
+
if depth > max_depth or not isinstance(fig_dict_or_subdict, dict):
|
2966
|
+
return fig_dict_or_subdict
|
2967
|
+
|
2968
|
+
for key, value in fig_dict_or_subdict.items():
|
2969
|
+
if key == "title": #This is for axes labels and graph title.
|
2970
|
+
if "text" in fig_dict_or_subdict[key]:
|
2971
|
+
fig_dict_or_subdict[key]["text"] = replace_superscripts(fig_dict_or_subdict[key]["text"])
|
2972
|
+
if key == "data": #This is for the legend.
|
2973
|
+
for data_dict in fig_dict_or_subdict[key]:
|
2974
|
+
if "name" in data_dict:
|
2975
|
+
data_dict["name"] = replace_superscripts(data_dict["name"])
|
2976
|
+
elif isinstance(value, dict): # Nested dictionary
|
2977
|
+
fig_dict_or_subdict[key] = update_superscripts_strings(value, depth + 1, max_depth)
|
2978
|
+
elif isinstance(value, list): # Lists can contain nested dictionaries
|
2979
|
+
fig_dict_or_subdict[key] = [update_superscripts_strings(item, depth + 1, max_depth) if isinstance(item, dict) else item for item in value]
|
2980
|
+
return fig_dict_or_subdict
|
2981
|
+
|
2982
|
+
#The below function was made with the help of copilot.
|
2983
|
+
def replace_superscripts(input_string):
|
2984
|
+
#Example usage: print(replace_superscripts("x^(2) + y**(-3) = z^(test)"))
|
2985
|
+
import re
|
2986
|
+
# Step 1: Wrap superscript expressions in <sup> tags
|
2987
|
+
output_string = re.sub(r'\^\((.*?)\)|\*\*\((.*?)\)',
|
2988
|
+
lambda m: f"<sup>{m.group(1) or m.group(2)}</sup>",
|
2989
|
+
input_string)
|
2990
|
+
# Step 2: Remove parentheses if the content is only digits
|
2991
|
+
output_string = re.sub(r'<sup>\((\d+)\)</sup>', r'<sup>\1</sup>', output_string)
|
2992
|
+
# Step 3: Remove parentheses if the content is a negative number (- followed by digits)
|
2993
|
+
# Step 4: Remove parentheses if the superscript is a single letter
|
2994
|
+
output_string = re.sub(r'<sup>\((\w)\)</sup>', r'<sup>\1</sup>', output_string)
|
2995
|
+
output_string = re.sub(r'<sup>\(-(\d+)\)</sup>', r'<sup>-\1</sup>', output_string)
|
2996
|
+
return output_string
|
2948
2997
|
|
2949
2998
|
|
2950
2999
|
def convert_to_3d_layout(layout):
|
@@ -3088,6 +3137,7 @@ def clean_json_fig_dict(json_fig_dict, fields_to_update=None):
|
|
3088
3137
|
because one would not want to do that by mistake before simulation is performed.
|
3089
3138
|
This function can also remove the 'equation' field from data series. However, that is not the default behavior
|
3090
3139
|
because one would not want to do that by mistake before the equation is evaluated.
|
3140
|
+
The "superscripts" option is not normally used until right before plotting because that will affect unit conversions.
|
3091
3141
|
"""
|
3092
3142
|
if fields_to_update is None: # should not initialize mutable objects in arguments line, so doing here.
|
3093
3143
|
fields_to_update = ["title_field", "extraInformation", "nested_comments"]
|
@@ -3111,6 +3161,8 @@ def clean_json_fig_dict(json_fig_dict, fields_to_update=None):
|
|
3111
3161
|
fig_dict = remove_trace_style_field(fig_dict)
|
3112
3162
|
if "3d_axes" in fields_to_update: #This is for 3D plots
|
3113
3163
|
fig_dict = update_3d_axes(fig_dict)
|
3164
|
+
if "superscripts" in fields_to_update:
|
3165
|
+
fig_dict = update_superscripts_strings(fig_dict)
|
3114
3166
|
|
3115
3167
|
return fig_dict
|
3116
3168
|
|
@@ -3118,6 +3170,8 @@ def clean_json_fig_dict(json_fig_dict, fields_to_update=None):
|
|
3118
3170
|
|
3119
3171
|
### Beginning of section of file that has functions for "simulate" and "equation" fields, to evaluate equations and call external javascript simulators, as well as support functions ###
|
3120
3172
|
|
3173
|
+
local_python_functions_dictionary = {} #This is a global variable that works with the "simulate" feature and lets users call python functions for data generation.
|
3174
|
+
|
3121
3175
|
def run_js_simulation(javascript_simulator_url, simulator_input_json_dict, verbose = False):
|
3122
3176
|
"""
|
3123
3177
|
Downloads a JavaScript file using its URL, extracts the filename, appends an export statement,
|
@@ -3227,7 +3281,7 @@ def convert_to_raw_github_url(url):
|
|
3227
3281
|
return url # Return unchanged if not a GitHub file URL
|
3228
3282
|
|
3229
3283
|
#This function takes in a data_series_dict object and then
|
3230
|
-
#calls an external javascript simulation if needed
|
3284
|
+
#calls an external python or javascript simulation if needed
|
3231
3285
|
#Then fills the data_series dict with the simulated data.
|
3232
3286
|
#This function is not intended to be called by the regular user
|
3233
3287
|
#because it returns extra fields that need to be parsed out.
|
@@ -3235,6 +3289,16 @@ def convert_to_raw_github_url(url):
|
|
3235
3289
|
def simulate_data_series(data_series_dict, simulator_link='', verbose=False):
|
3236
3290
|
if simulator_link == '':
|
3237
3291
|
simulator_link = data_series_dict["simulate"]["model"]
|
3292
|
+
if simulator_link == "local_python": #this is the local python case.
|
3293
|
+
#Here, I haev split up the lines of code more than needed so that the logic is easy to follow.
|
3294
|
+
simulation_function_label = data_series_dict["simulate"]["simulation_function_label"]
|
3295
|
+
simulation_function = local_python_functions_dictionary[simulation_function_label]
|
3296
|
+
simulation_return = simulation_function(data_series_dict)
|
3297
|
+
if "data" in simulation_return: #the simulation return should have the data_series_dict in another dictionary.
|
3298
|
+
simulation_result = simulation_return["data"]
|
3299
|
+
else: #if there is no "data" field, we will assume that only the data_series_dict has been returned.
|
3300
|
+
simulation_result = simulation_return
|
3301
|
+
return simulation_result
|
3238
3302
|
try:
|
3239
3303
|
simulation_return = run_js_simulation(simulator_link, data_series_dict, verbose=verbose)
|
3240
3304
|
if isinstance(simulation_return, dict) and "error" in simulation_return: # Check for errors in the returned data
|
@@ -1,11 +1,11 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: jsongrapher
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.9
|
4
4
|
Summary: The python version of JSONGrapher with tools for creating JSONGrapher Records.
|
5
5
|
Home-page: https://github.com/AdityaSavara/jsongrapher-py
|
6
6
|
Author: Aditya Savara
|
7
|
-
Author-email:
|
8
|
-
License:
|
7
|
+
Author-email: AdityaSavara2008@u.northwestern.edu
|
8
|
+
License: BSD-3-Clause
|
9
9
|
Classifier: License :: OSI Approved :: BSD License
|
10
10
|
Classifier: Programming Language :: Python
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
@@ -1,4 +1,4 @@
|
|
1
|
-
JSONGrapher/JSONRecordCreator.py,sha256=
|
1
|
+
JSONGrapher/JSONRecordCreator.py,sha256=nENtRcr0hlysqhRIc-BhYRE8VN9Q8TaB0IC74jiKUzw,209780
|
2
2
|
JSONGrapher/UnitPytesting.py,sha256=xizJ-2fg9C5oNMFJyfavbBLMusayE9KWQiYIRrQQd4A,4363
|
3
3
|
JSONGrapher/UnitpyCustomUnitsTesting.py,sha256=Rwq5p8HXN0FP54lRFgLTV0kgJ9TpcFaD3_C0MEOoEzw,1297
|
4
4
|
JSONGrapher/__init__.py,sha256=cc5qXQidP_ABPXdnXy_DTdgPanHuR7ya45LV-BdWVh8,277
|
@@ -9,10 +9,10 @@ JSONGrapher/units_list.py,sha256=ROrlAsD66oPozZmOaE65xjNUEg3CxkSmA8iPE2_ihYI,344
|
|
9
9
|
JSONGrapher/styles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
JSONGrapher/styles/layout_styles_library.py,sha256=vGb1tE_ETY-Blj2frt8ddHA976ADFSCoabjzKn2ZNYU,3051
|
11
11
|
JSONGrapher/styles/trace_styles_collection_library.py,sha256=DUbbO8jTlhn2q-lOY1XYGkHV1RbJX-P5Z1SUCM0qo6M,5952
|
12
|
-
jsongrapher-3.
|
13
|
-
jsongrapher-3.
|
14
|
-
jsongrapher-3.
|
15
|
-
jsongrapher-3.
|
16
|
-
jsongrapher-3.
|
17
|
-
jsongrapher-3.
|
18
|
-
jsongrapher-3.
|
12
|
+
jsongrapher-3.9.data/data/LICENSE,sha256=MroL1bQKoAHrMJv2KvABMmZX5j-DZ2EP_zaQacLUtj0,1465
|
13
|
+
jsongrapher-3.9.data/data/README.md,sha256=OntfAICB_uwfjoq8tMDYc_W7eXSC2N22X0udPIGb2Nw,5888
|
14
|
+
jsongrapher-3.9.dist-info/LICENSE,sha256=MroL1bQKoAHrMJv2KvABMmZX5j-DZ2EP_zaQacLUtj0,1465
|
15
|
+
jsongrapher-3.9.dist-info/METADATA,sha256=q1LGWAVZNTTawUuuWZzy8WypNwQv7tdYWNIt1xWSbsE,6960
|
16
|
+
jsongrapher-3.9.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
17
|
+
jsongrapher-3.9.dist-info/top_level.txt,sha256=5f7Ui2hCKCPTQOjD540WKghKNYOvUDNfdpnDbIlAD3Y,12
|
18
|
+
jsongrapher-3.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|