jsongrapher 3.8__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.
@@ -831,6 +831,7 @@ class JSONGrapherRecord:
831
831
  def evaluate_eqution_of_data_series_by_index(self, series_index, equation_dict = None, verbose=False):
832
832
  if equation_dict != None:
833
833
  self.fig_dict["data"][series_index]["equation"] = equation_dict
834
+ data_series_dict = self.fig_dict["data"][series_index]
834
835
  self.fig_dict = evaluate_equation_for_data_series_by_index(data_series_index=data_series_dict, verbose=verbose) #implied return.
835
836
  return data_series_dict #Extra regular return
836
837
 
@@ -842,11 +843,11 @@ class JSONGrapherRecord:
842
843
  return self.fig_dict
843
844
  #The update_and_validate function will clean for plotly.
844
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.
845
- 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):
846
847
  if remove_remaining_hints == True:
847
848
  self.remove_hints()
848
849
  if update_and_validate == True: #this will do some automatic 'corrections' during the validation.
849
- self.update_and_validate_JSONGrapher_record()
850
+ self.update_and_validate_JSONGrapher_record(clean_for_plotly=clean_for_plotly)
850
851
  elif validate: #this will validate without doing automatic updates.
851
852
  self.validate_JSONGrapher_record()
852
853
  print(json.dumps(self.fig_dict, indent=4))
@@ -896,9 +897,7 @@ class JSONGrapherRecord:
896
897
  #now, add the scaled data objects to the original one.
897
898
  #This is fairly easy using a list extend.
898
899
  self.fig_dict["data"].extend(scaled_fig_dict["data"])
899
-
900
-
901
-
900
+
902
901
  def import_from_dict(self, fig_dict):
903
902
  self.fig_dict = fig_dict
904
903
 
@@ -1047,7 +1046,7 @@ class JSONGrapherRecord:
1047
1046
  elif validate: #this will validate without doing automatic updates.
1048
1047
  self.validate_JSONGrapher_record()
1049
1048
 
1050
- # filepath: Optional, filename with path to save the JSON file.
1049
+ # filename with path to save the JSON file.
1051
1050
  if len(filename) > 0: #this means we will be writing to file.
1052
1051
  # Check if the filename has an extension and append `.json` if not
1053
1052
  if '.json' not in filename.lower():
@@ -1057,6 +1056,18 @@ class JSONGrapherRecord:
1057
1056
  json.dump(self.fig_dict, f, indent=4)
1058
1057
  return self.fig_dict
1059
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
+
1060
1071
  #simulate all series will simulate any series as needed.
1061
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):
1062
1073
  """
@@ -1092,8 +1103,8 @@ class JSONGrapherRecord:
1092
1103
  self.apply_plot_style(plot_style=plot_style)
1093
1104
  #Now we clean out the fields and make a plotly object.
1094
1105
  if update_and_validate == True: #this will do some automatic 'corrections' during the validation.
1095
- self.update_and_validate_JSONGrapher_record() #this is the line that cleans "self.fig_dict"
1096
- 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'])
1097
1108
  fig = pio.from_json(json.dumps(self.fig_dict))
1098
1109
  #restore the original fig_dict.
1099
1110
  self.fig_dict = original_fig_dict
@@ -1390,8 +1401,8 @@ class JSONGrapherRecord:
1390
1401
  #Make some pointers to external functions, for convenience, so people can use syntax like record.function_name() if desired.
1391
1402
  def validate_JSONGrapher_record(self):
1392
1403
  validate_JSONGrapher_record(self)
1393
- def update_and_validate_JSONGrapher_record(self):
1394
- 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)
1395
1406
 
1396
1407
 
1397
1408
  # helper function to validate x axis and y axis labels.
@@ -2928,24 +2939,61 @@ def get_fig_dict_ranges(fig_dict, skip_equations=False, skip_simulations=False):
2928
2939
 
2929
2940
  ### Start section of code with functions for cleaning fig_dicts for plotly compatibility ###
2930
2941
 
2931
- def update_title_field(fig_dict, depth=1, max_depth=10):
2942
+ def update_title_field(fig_dict_or_subdict, depth=1, max_depth=10):
2932
2943
  """ This function is intended to make JSONGrapher .json files compatible with the newer plotly recommended title field formatting
2933
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.
2934
2945
  Recursively checks for 'title' fields and converts them to dictionary format. """
2935
- if depth > max_depth or not isinstance(fig_dict, dict):
2936
- return fig_dict
2946
+ if depth > max_depth or not isinstance(fig_dict_or_subdict, dict):
2947
+ return fig_dict_or_subdict
2937
2948
 
2938
- for key, value in fig_dict.items():
2939
- if key == "title" and isinstance(value, str):
2940
- fig_dict[key] = {"text": value}
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}
2941
2952
  elif isinstance(value, dict): # Nested dictionary
2942
- fig_dict[key] = update_title_field(value, depth + 1, max_depth)
2953
+ fig_dict_or_subdict[key] = update_title_field(value, depth + 1, max_depth)
2943
2954
  elif isinstance(value, list): # Lists can contain nested dictionaries
2944
- fig_dict[key] = [update_title_field(item, depth + 1, max_depth) if isinstance(item, dict) else item for item in value]
2945
- return fig_dict
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
+
2946
2958
 
2947
2959
 
2948
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
2949
2997
 
2950
2998
 
2951
2999
  def convert_to_3d_layout(layout):
@@ -3089,6 +3137,7 @@ def clean_json_fig_dict(json_fig_dict, fields_to_update=None):
3089
3137
  because one would not want to do that by mistake before simulation is performed.
3090
3138
  This function can also remove the 'equation' field from data series. However, that is not the default behavior
3091
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.
3092
3141
  """
3093
3142
  if fields_to_update is None: # should not initialize mutable objects in arguments line, so doing here.
3094
3143
  fields_to_update = ["title_field", "extraInformation", "nested_comments"]
@@ -3112,6 +3161,8 @@ def clean_json_fig_dict(json_fig_dict, fields_to_update=None):
3112
3161
  fig_dict = remove_trace_style_field(fig_dict)
3113
3162
  if "3d_axes" in fields_to_update: #This is for 3D plots
3114
3163
  fig_dict = update_3d_axes(fig_dict)
3164
+ if "superscripts" in fields_to_update:
3165
+ fig_dict = update_superscripts_strings(fig_dict)
3115
3166
 
3116
3167
  return fig_dict
3117
3168
 
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jsongrapher
3
- Version: 3.8
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: AditySavara2008@u.northwestern.edu
7
+ Author-email: AdityaSavara2008@u.northwestern.edu
8
8
  License: BSD-3-Clause
9
9
  Classifier: License :: OSI Approved :: BSD License
10
10
  Classifier: Programming Language :: Python
@@ -1,4 +1,4 @@
1
- JSONGrapher/JSONRecordCreator.py,sha256=SFIYYNDpsUAx6vYA-I2ZGxNw7a1yA3yoIEE6oZtZWfc,205808
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.8.data/data/LICENSE,sha256=MroL1bQKoAHrMJv2KvABMmZX5j-DZ2EP_zaQacLUtj0,1465
13
- jsongrapher-3.8.data/data/README.md,sha256=OntfAICB_uwfjoq8tMDYc_W7eXSC2N22X0udPIGb2Nw,5888
14
- jsongrapher-3.8.dist-info/LICENSE,sha256=MroL1bQKoAHrMJv2KvABMmZX5j-DZ2EP_zaQacLUtj0,1465
15
- jsongrapher-3.8.dist-info/METADATA,sha256=IZYNGMd9jbDyWIoyqhW1oUan16p04jf228OW4h7wMrk,6959
16
- jsongrapher-3.8.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
17
- jsongrapher-3.8.dist-info/top_level.txt,sha256=5f7Ui2hCKCPTQOjD540WKghKNYOvUDNfdpnDbIlAD3Y,12
18
- jsongrapher-3.8.dist-info/RECORD,,
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,,