pyeasyphd 0.4.42__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.
Files changed (53) hide show
  1. pyeasyphd/.python-version +1 -0
  2. pyeasyphd/Main.sublime-menu +43 -0
  3. pyeasyphd/__init__.py +5 -0
  4. pyeasyphd/data/templates/csl/apa-no-ampersand.csl +2183 -0
  5. pyeasyphd/data/templates/csl/apa.csl +2133 -0
  6. pyeasyphd/data/templates/csl/ieee.csl +512 -0
  7. pyeasyphd/data/templates/tex/Article.tex +38 -0
  8. pyeasyphd/data/templates/tex/Article_Header.tex +29 -0
  9. pyeasyphd/data/templates/tex/Article_Tail.tex +3 -0
  10. pyeasyphd/data/templates/tex/Beamer_Header.tex +79 -0
  11. pyeasyphd/data/templates/tex/Beamer_Tail.tex +14 -0
  12. pyeasyphd/data/templates/tex/Style.tex +240 -0
  13. pyeasyphd/data/templates/tex/TEVC_Header.tex +52 -0
  14. pyeasyphd/data/templates/tex/TEVC_Tail.tex +4 -0
  15. pyeasyphd/data/templates/tex/eisvogel.tex +1064 -0
  16. pyeasyphd/data/templates/tex/math.tex +201 -0
  17. pyeasyphd/data/templates/tex/math_commands.tex +677 -0
  18. pyeasyphd/data/templates/tex/nextaimathmacros.sty +681 -0
  19. pyeasyphd/main/__init__.py +6 -0
  20. pyeasyphd/main/basic_input.py +101 -0
  21. pyeasyphd/main/pandoc_md_to.py +380 -0
  22. pyeasyphd/main/python_run_md.py +320 -0
  23. pyeasyphd/main/python_run_tex.py +200 -0
  24. pyeasyphd/pyeasyphd.py +86 -0
  25. pyeasyphd/pyeasyphd.sublime-settings +100 -0
  26. pyeasyphd/pyeasyphd.sublime-syntax +5 -0
  27. pyeasyphd/scripts/__init__.py +34 -0
  28. pyeasyphd/scripts/_base.py +65 -0
  29. pyeasyphd/scripts/run_article_md.py +101 -0
  30. pyeasyphd/scripts/run_article_tex.py +94 -0
  31. pyeasyphd/scripts/run_beamer_tex.py +84 -0
  32. pyeasyphd/scripts/run_compare.py +71 -0
  33. pyeasyphd/scripts/run_format.py +62 -0
  34. pyeasyphd/scripts/run_generate.py +211 -0
  35. pyeasyphd/scripts/run_replace.py +34 -0
  36. pyeasyphd/scripts/run_search.py +251 -0
  37. pyeasyphd/tools/__init__.py +12 -0
  38. pyeasyphd/tools/generate/generate_from_bibs.py +181 -0
  39. pyeasyphd/tools/generate/generate_html.py +166 -0
  40. pyeasyphd/tools/generate/generate_library.py +203 -0
  41. pyeasyphd/tools/generate/generate_links.py +400 -0
  42. pyeasyphd/tools/py_run_bib_md_tex.py +398 -0
  43. pyeasyphd/tools/search/data.py +282 -0
  44. pyeasyphd/tools/search/search_base.py +146 -0
  45. pyeasyphd/tools/search/search_core.py +400 -0
  46. pyeasyphd/tools/search/search_keywords.py +229 -0
  47. pyeasyphd/tools/search/search_writers.py +350 -0
  48. pyeasyphd/tools/search/utils.py +190 -0
  49. pyeasyphd/utils/utils.py +99 -0
  50. pyeasyphd-0.4.42.dist-info/METADATA +33 -0
  51. pyeasyphd-0.4.42.dist-info/RECORD +53 -0
  52. pyeasyphd-0.4.42.dist-info/WHEEL +4 -0
  53. pyeasyphd-0.4.42.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,320 @@
1
+ import copy
2
+ import os
3
+ import re
4
+ import shutil
5
+ import time
6
+ from typing import Any
7
+
8
+ from pyadvtools import combine_content_in_list, delete_empty_lines_last_occur_add_new_line, read_list, write_list
9
+ from pybibtexer.bib.core import ConvertStrToLibrary
10
+ from pybibtexer.main.python_writers import PythonWriters
11
+
12
+ from .basic_input import BasicInput
13
+ from .pandoc_md_to import PandocMdTo
14
+
15
+
16
+ def batch_convert_citations(text):
17
+ r"""Process all citations in the text, including multiple citations in one bracket.
18
+
19
+ Example: [@ref1; @ref2] -> <sup>[@ref1](#ref1)</sup><sup>[@ref2](#ref2)</sup>
20
+ """
21
+ # Match all citation patterns within square brackets
22
+ pattern = r"\[([^]]+)\]"
23
+
24
+ def process_citation(match):
25
+ citations = match.group(1)
26
+ # Split multiple citations (support semicolon or comma separation)
27
+ citation_list = re.split(r"[;,]", citations)
28
+
29
+ result = []
30
+ for citation in citation_list:
31
+ citation = citation.strip()
32
+ if citation.startswith("@"):
33
+ cite_id = citation[1:] # Remove the @ symbol
34
+ result.append(f"[{citation}](#{cite_id.lower()})")
35
+ else:
36
+ # Keep non-citation content in original format
37
+ result.append(f"[{citation}]")
38
+
39
+ return "".join(result)
40
+
41
+ return re.sub(pattern, process_citation, text)
42
+
43
+
44
+ class PythonRunMd(BasicInput):
45
+ r"""Python markdown processing class.
46
+
47
+ Args:
48
+ options (dict[str, Any]): Configuration options.
49
+
50
+ Attributes:
51
+ delete_temp_generate_md (bool): Whether to delete temporary generated markdown files. Defaults to True.
52
+ add_reference_in_md (bool): Whether to add references in markdown. Defaults to True.
53
+ add_bib_in_md (bool): Whether to add bibliography in markdown. Defaults to False.
54
+ replace_cite_to_fullcite_in_md (bool): Whether to replace citations with full citations in markdown. Defaults to False.
55
+ replace_by_basic_beauty_complex_in_md (str): Replace by basic, beauty, or complex format. Defaults to "beauty".
56
+ display_basic_beauty_complex_references_in_md (str): Display basic, beauty, or complex references. Defaults to "beauty".
57
+ add_anchor_in_md (bool): Whether add anchor in markdown. Defaults to False.
58
+ details_to_bib_separator (str): Separator between <details> and bibliography content. Defaults to "\n".
59
+ """
60
+
61
+ def __init__(self, options: dict[str, Any]) -> None:
62
+ """Initialize PythonRunMd with configuration options.
63
+
64
+ Args:
65
+ options (dict[str, Any]): Configuration options.
66
+ """
67
+ super().__init__(options)
68
+
69
+ # for md
70
+ self.final_output_main_md_name: str = options.get("final_output_main_md_name", "")
71
+ self.delete_temp_generate_md: bool = options.get("delete_temp_generate_md", True)
72
+ self.add_reference_in_md: bool = options.get("add_reference_in_md", True)
73
+ self.add_bib_in_md: bool = options.get("add_bib_in_md", False)
74
+ self.replace_cite_to_fullcite_in_md: bool = options.get("replace_cite_to_fullcite_in_md", False)
75
+ self.replace_by_basic_beauty_complex_in_md: str = options.get("replace_by_basic_beauty_complex_in_md", "beauty")
76
+ self.display_basic_beauty_complex_references_in_md: str = options.get(
77
+ "display_basic_beauty_complex_references_in_md", "beauty"
78
+ )
79
+ self.add_anchor_in_md: bool = options.get("add_anchor_in_md", False)
80
+ self.details_to_bib_separator: str = options.get("details_to_bib_separator", "\n")
81
+
82
+ # for md
83
+ self._pandoc_md_to = PandocMdTo(self.options)
84
+
85
+ _options = {}
86
+ _options["is_standardize_bib"] = False
87
+ _options["is_display_implicit_comments"] = False
88
+ _options.update(options)
89
+ self._generate_library = ConvertStrToLibrary(_options)
90
+
91
+ def special_operate_for_md(
92
+ self,
93
+ path_output: str,
94
+ data_list_md: list[str],
95
+ output_md_name: str,
96
+ full_bib_for_abbr: str,
97
+ full_bib_for_zotero: str,
98
+ template_name: str = "article",
99
+ generate_html: bool = False,
100
+ generate_tex: bool = True,
101
+ ) -> tuple[list[str], list[str]]:
102
+ """Perform special operations on markdown files.
103
+
104
+ Args:
105
+ path_output (str): Path to output directory.
106
+ data_list_md (list[str]): list of markdown content lines.
107
+ output_md_name (str): Name of output markdown file.
108
+ full_bib_for_abbr (str): Path to abbreviated bibliography file.
109
+ full_bib_for_zotero (str): Path to Zotero bibliography file.
110
+ template_name (str): Name of template to use. Defaults to "article".
111
+ generate_html (bool): Whether to generate HTML. Defaults to False.
112
+ generate_tex (bool): Whether to generate LaTeX. Defaults to True.
113
+
114
+ Returns:
115
+ tuple[list[str], list[str]]: Tuple containing processed markdown and LaTeX content.
116
+ """
117
+ path_temp = os.path.join(path_output, "{}".format(time.strftime("%Y_%m_%d_%H_%M_%S")))
118
+ if not os.path.exists(path_temp):
119
+ os.makedirs(path_temp)
120
+
121
+ # write original md content to temp file
122
+ full_temp_md = os.path.join(path_temp, output_md_name)
123
+ write_list(data_list_md, full_temp_md, "w", None, False)
124
+
125
+ # pandoc md to md to update md content
126
+ if read_list(full_bib_for_abbr, "r") and read_list(full_bib_for_zotero, "r"):
127
+ data_list_md = self._special_operate_for_md(
128
+ output_md_name, path_temp, full_bib_for_abbr, full_bib_for_zotero
129
+ )
130
+ elif os.path.exists(full_bib_for_abbr) and os.path.exists(full_bib_for_zotero):
131
+ print(f"The content of bib: {full_bib_for_abbr} or {full_bib_for_zotero} is empty.")
132
+ else:
133
+ pass
134
+
135
+ # main name
136
+ main_name = self.final_output_main_md_name
137
+ if len(main_name) == 0:
138
+ main_name = output_md_name.split(".md")[0] + "_" + self.replace_by_basic_beauty_complex_in_md
139
+ main_name = main_name + "_" + self.display_basic_beauty_complex_references_in_md + ".md"
140
+ if main_name.lower() == output_md_name.lower():
141
+ main_name = main_name.split(".md")[0] + "_.md"
142
+ if main_name[-3:] != ".md":
143
+ main_name = main_name + ".md"
144
+
145
+ # write new generated md content to given file
146
+ write_list(data_list_md, main_name, "w", path_output, False)
147
+
148
+ # for generating html file from md file
149
+ if data_list_md and generate_html:
150
+ self._pandoc_md_to.pandoc_md_to_html(
151
+ path_output, path_output, main_name, f"{main_name.split('.md')[0]}.html", True
152
+ )
153
+
154
+ # pandoc md to latex
155
+ data_list_tex = []
156
+ if generate_tex:
157
+ n5 = "5_pandoc" + ".tex"
158
+ data_list_tex = self._pandoc_md_to.pandoc_md_to_tex(template_name, path_temp, path_temp, output_md_name, n5)
159
+
160
+ # delete temp path
161
+ if self.delete_temp_generate_md:
162
+ shutil.rmtree(path_temp)
163
+ return data_list_md, data_list_tex
164
+
165
+ def _special_operate_for_md(
166
+ self, output_md_name: str, path_temp: str, full_bib_for_abbr: str, full_bib_for_zotero: str
167
+ ) -> list[str]:
168
+ """Perform special operations for markdown processing.
169
+
170
+ Args:
171
+ output_md_name (str): Name of output markdown file.
172
+ path_temp (str): Path to temporary directory.
173
+ full_bib_for_abbr (str): Path to abbreviated bibliography file.
174
+ full_bib_for_zotero (str): Path to Zotero bibliography file.
175
+
176
+ Returns:
177
+ list[str]: list of processed markdown content lines.
178
+ """
179
+ # pandoc markdown to markdown
180
+ n1 = "1_pandoc" + ".md"
181
+ data_list_md = self._pandoc_md_to.pandoc_md_to_md(full_bib_for_abbr, path_temp, path_temp, output_md_name, n1)
182
+
183
+ # use zotero bib to generate library
184
+ bib_for_zotero = read_list(full_bib_for_zotero, "r")
185
+ library = self._generate_library.generate_library(bib_for_zotero)
186
+
187
+ _options = {}
188
+ _options.update(self.options)
189
+ _options["add_index_to_enties"] = False
190
+ _python_writers = PythonWriters(_options)
191
+ key_url_http_bib_dict = _python_writers.output_key_url_http_bib_dict(library)
192
+
193
+ content_md = []
194
+ if data_list_md and key_url_http_bib_dict:
195
+ key_basic_dict, key_beauty_dict, key_complex_dict = self._pandoc_md_to.generate_key_data_dict(
196
+ data_list_md, key_url_http_bib_dict
197
+ )
198
+
199
+ key_in_md = list(key_url_http_bib_dict.keys())
200
+
201
+ # generate by replacing `- [@citation_key]` to `- [citation_key]`
202
+ content = read_list(output_md_name, "r", path_temp)
203
+ if self.replace_cite_to_fullcite_in_md:
204
+ regex = re.compile(r"(\s*[-\+\*]\s*)\[@({})\]".format("|".join(key_in_md)))
205
+ for i in range(len(content)):
206
+ if not (mch := regex.match(content[i])):
207
+ continue
208
+
209
+ content[i] = content[i].replace(mch.group(), mch.group(1) + "[" + mch.group(2) + "]")
210
+ # add anchor
211
+ if self.add_anchor_in_md:
212
+ content = [batch_convert_citations(line) for line in content]
213
+ n2 = "2_generate" + ".md"
214
+ write_list(content, n2, "w", path_temp)
215
+
216
+ # pandoc markdown to markdown
217
+ n3 = "3_pandoc" + ".md"
218
+ data_list_md = self._pandoc_md_to.pandoc_md_to_md(full_bib_for_abbr, path_temp, path_temp, n2, n3)
219
+
220
+ # generate by replacing `- [citation_key]` to `- reference`
221
+ if self.replace_cite_to_fullcite_in_md:
222
+ regex = re.compile(r"(\s*)([-\+\*])(\s*)[\\]*\[({})[\\]*\]".format("|".join(key_in_md)))
223
+ for i in range(len(data_list_md)):
224
+ if not (mch := regex.search(data_list_md[i])):
225
+ continue
226
+
227
+ space_one, b, space_two, cite_key = mch.groups()
228
+ if self.replace_by_basic_beauty_complex_in_md.lower() == "basic":
229
+ temp_list = copy.deepcopy(key_basic_dict[cite_key])
230
+ elif self.replace_by_basic_beauty_complex_in_md.lower() == "complex":
231
+ temp_list = copy.deepcopy(key_complex_dict[cite_key])
232
+ else:
233
+ temp_list = copy.deepcopy(key_beauty_dict[cite_key])
234
+
235
+ temp = "".join(self._special_format(temp_list, space_one, space_two))
236
+ data_list_md[i] = data_list_md[i].replace(mch.group(), space_one + b + space_two + temp.strip())
237
+ n4 = "4_generate" + ".md"
238
+ write_list(data_list_md, os.path.join(path_temp, n4), "w")
239
+
240
+ # obtain footnote part (in the last part of the contents)
241
+ main_part, last_part = [], []
242
+ main_part_flag, last_part_flag = True, True
243
+ for line_index in range(len(data_list_md)):
244
+ if main_part_flag and re.match(r"#+ [\"']?References[\"']?\s[\[{]", data_list_md[line_index]):
245
+ main_part_flag = False
246
+ main_part = delete_empty_lines_last_occur_add_new_line(data_list_md[:line_index])
247
+ if last_part_flag and re.match(r"^\[\^1]: ", data_list_md[line_index]):
248
+ last_part_flag = False
249
+ last_part = delete_empty_lines_last_occur_add_new_line(data_list_md[line_index:])
250
+
251
+ if main_part_flag:
252
+ main_part = delete_empty_lines_last_occur_add_new_line(data_list_md)
253
+ if self.add_reference_in_md:
254
+ main_part.append("\n## References\n")
255
+ if len(last_part) > 0:
256
+ last_part.insert(0, "[//]: (Footnotes)\n\n")
257
+ last_part.append("\n")
258
+
259
+ # for bib
260
+ bib_in_md = []
261
+ if self.add_bib_in_md:
262
+ temp_c = combine_content_in_list([key_url_http_bib_dict[k][2] for k in key_in_md])
263
+ bib_in_md = combine_content_in_list(
264
+ [
265
+ ["## Bibliography\n\n"],
266
+ [f"<details>{self.details_to_bib_separator}"],
267
+ ["```\n"],
268
+ temp_c,
269
+ ["```\n"],
270
+ ["</details>\n"],
271
+ ]
272
+ )
273
+
274
+ # Generate basic/beauty/complex markdown content
275
+ if dct := eval(f"key_{self.display_basic_beauty_complex_references_in_md}_dict"):
276
+ content_md = self._generate_content_md(dct, key_in_md, main_part, last_part, bib_in_md)
277
+ return content_md
278
+
279
+ @staticmethod
280
+ def _special_format(temp_list: list[str], space_one: str, space_two: str) -> list[str]:
281
+ """Apply special formatting for alignment.
282
+
283
+ Args:
284
+ temp_list (list[str]): list of strings to format.
285
+ space_one (str): First space string.
286
+ space_two (str): Second space string.
287
+
288
+ Returns:
289
+ list[str]: Formatted list of strings.
290
+ """
291
+ for j in range(len(temp_list) - 1):
292
+ if temp_list[j][-1] == "\n":
293
+ temp_list[j + 1] = (space_one + " " + space_two) + temp_list[j + 1]
294
+ return temp_list
295
+
296
+ def _generate_content_md(
297
+ self,
298
+ key_basic_beauty_complex_dict: dict[str, list[str]],
299
+ key_in_md_tex: list[str],
300
+ main_part: list[str],
301
+ last_part: list[str],
302
+ bib_in_md: list[str],
303
+ ) -> list[str]:
304
+ """Generate markdown content from various components.
305
+
306
+ Args:
307
+ key_basic_beauty_complex_dict (dict[str, list[str]]): dictionary of formatted references.
308
+ key_in_md_tex (list[str]): list of citation keys in markdown/LaTeX.
309
+ main_part (list[str]): Main content part.
310
+ last_part (list[str]): Last content part.
311
+ bib_in_md (list[str]): Bibliography content for markdown.
312
+
313
+ Returns:
314
+ list[str]: Generated markdown content.
315
+ """
316
+ temp_b = []
317
+ if self.add_reference_in_md:
318
+ temp_b = combine_content_in_list([key_basic_beauty_complex_dict[k] for k in key_in_md_tex], ["\n"])
319
+ content_md = combine_content_in_list([main_part, ["\n"], temp_b, last_part, bib_in_md])
320
+ return content_md
@@ -0,0 +1,200 @@
1
+ import os
2
+ import re
3
+ import shutil
4
+ import subprocess
5
+ from typing import Any
6
+
7
+ from pyadvtools import delete_files, insert_list_in_list, read_list, write_list
8
+
9
+ from .basic_input import BasicInput
10
+
11
+
12
+ class PythonRunTex(BasicInput):
13
+ """Python LaTeX document processing and compilation class.
14
+
15
+ This class extends BasicInput to handle LaTeX-specific operations including
16
+ document compilation, output management, and temporary file cleanup.
17
+ It supports multiple LaTeX engines and provides configurable cleanup options.
18
+
19
+ Args:
20
+ options (dict[str, Any]): Configuration options for LaTeX processing.
21
+
22
+ Attributes:
23
+ final_output_main_tex_name (str): Name of the main output LaTeX file.
24
+ Defaults to empty string.
25
+ run_latex (bool): Flag indicating whether to execute LaTeX compilation.
26
+ When True, the system will compile the LaTeX document. Defaults to False.
27
+ pdflatex_xelatex (str): LaTeX engine selection. Valid options are
28
+ 'pdflatex' for standard PDF compilation or 'xelatex' for extended
29
+ Unicode and font support. Defaults to "xelatex".
30
+ delete_run_latex_cache (bool): Flag controlling cleanup of temporary
31
+ LaTeX files after compilation. When True, auxiliary files are removed.
32
+ Defaults to True.
33
+ latex_clean_file_types (list[str] | None): List of file extensions to
34
+ remove during cleanup. If None, a default set is used. Defaults to None.
35
+ replace_duplicate_output_tex_file (bool): Flag indicating whether to
36
+ overwrite existing output files with the same name. When True,
37
+ duplicates are replaced; when False, new names may be generated.
38
+ Defaults to False.
39
+ """
40
+
41
+ def __init__(self, options: dict[str, Any]) -> None:
42
+ """Initialize PythonRunTex with configuration options.
43
+
44
+ Args:
45
+ options (dict[str, Any]): Configuration options.
46
+ """
47
+ super().__init__(options)
48
+
49
+ # for tex
50
+ self.final_output_main_tex_name: str = options.get("final_output_main_tex_name", "")
51
+ self.run_latex: bool = options.get("run_latex", False)
52
+ self.pdflatex_xelatex: str = options.get("pdflatex_xelatex", "xelatex") # pdflatex, xelatex
53
+ self.delete_run_latex_cache: bool = options.get("delete_run_latex_cache", True)
54
+ self.latex_clean_file_types: list[str] | None = options.get("latex_clean_file_types", None)
55
+ self.replace_duplicate_output_tex_file: bool = options.get("replace_duplicate_output_tex_file", False)
56
+
57
+ def generate_standard_tex_data_list(
58
+ self,
59
+ data_list_body: list[str],
60
+ output_tex_name: str,
61
+ path_output: str,
62
+ figure_folder_name: str = "figs",
63
+ tex_folder_name: str = "tex",
64
+ bib_folder_name: str = "bib",
65
+ bib_name: str = "abbr.bib",
66
+ template_name: str = "article",
67
+ ) -> list[str]:
68
+ """Generate standard LaTeX data list with proper formatting.
69
+
70
+ Args:
71
+ data_list_body (list[str]): list of body content strings.
72
+ output_tex_name (str): Name of output LaTeX file.
73
+ path_output (str): Path to output directory.
74
+ figure_folder_name (str): Name of figures folder. Defaults to "figs".
75
+ tex_folder_name (str): Name of LaTeX folder. Defaults to "tex".
76
+ bib_folder_name (str): Name of bibliography folder. Defaults to "bib".
77
+ bib_name (str): Name of bibliography file. Defaults to "abbr.bib".
78
+ template_name (str): Name of template to use. Defaults to "article".
79
+
80
+ Returns:
81
+ list[str]: list of processed LaTeX content strings.
82
+ """
83
+ # for figures
84
+ for i in range(len(data_list_body)):
85
+ if re.search(r"\\includegraphics", data_list_body[i]):
86
+ data_list_body[i] = data_list_body[i].replace(
87
+ f".{os.sep}Figures{os.sep}", f".{os.sep}{figure_folder_name}{os.sep}"
88
+ )
89
+ data_list_body[i] = data_list_body[i].replace(f"Figures{os.sep}", f"{figure_folder_name}{os.sep}")
90
+ write_list(data_list_body, output_tex_name, "w", os.path.join(path_output, tex_folder_name), False)
91
+
92
+ self._special_operate_tex(
93
+ data_list_body, template_name, output_tex_name, path_output, tex_folder_name, bib_folder_name, bib_name
94
+ )
95
+ return data_list_body
96
+
97
+ def _special_operate_tex(
98
+ self,
99
+ data_list_body: list[str],
100
+ template_name: str,
101
+ output_tex_name: str,
102
+ path_output: str,
103
+ tex_folder_name: str,
104
+ bib_folder_name: str,
105
+ bib_name: str,
106
+ ) -> None:
107
+ """Perform special operations on LaTeX files.
108
+
109
+ Args:
110
+ data_list_body (list[str]): list of body content strings.
111
+ template_name (str): Name of template to use.
112
+ output_tex_name (str): Name of output LaTeX file.
113
+ path_output (str): Path to output directory.
114
+ tex_folder_name (str): Name of LaTeX folder.
115
+ bib_folder_name (str): Name of bibliography folder.
116
+ bib_name (str): Name of bibliography file.
117
+ """
118
+ # read template data
119
+ template_h, template_t = [], []
120
+ if template_name.lower() == "paper":
121
+ template_h, template_t = self.article_template_header_tex, self.article_template_tail_tex
122
+ elif template_name.lower() == "beamer":
123
+ template_h, template_t = self.beamer_template_header_tex, self.beamer_template_tail_tex
124
+
125
+ # style
126
+ if usepackages := self.usepackages_tex:
127
+ usepackages.insert(0, "\n")
128
+
129
+ # command
130
+ if math_commands := self.math_commands_tex:
131
+ math_commands.insert(0, "\n")
132
+
133
+ # main name
134
+ main_name = self.final_output_main_tex_name
135
+ if len(main_name) == 0:
136
+ main_name = output_tex_name.split(".text")[0] + "_main.tex"
137
+ if (not self.replace_duplicate_output_tex_file) and (main_name.lower() == output_tex_name.lower()):
138
+ main_name = main_name.split(".tex")[0] + "_.tex"
139
+ if main_name[-4:] != ".tex":
140
+ main_name = main_name + ".tex"
141
+
142
+ data_list = []
143
+ if (len(template_h) != 0) and (len(template_t) != 0):
144
+ # header
145
+ data_list = insert_list_in_list(template_h, usepackages, r"\\documentclass", "after")
146
+ data_list = insert_list_in_list(data_list, math_commands, r"\\documentclass", "after")
147
+
148
+ if template_name.lower() == "beamer":
149
+ data_list = insert_list_in_list(data_list, ["\n\\def\\allfiles{}\n"], r"\\documentclass", "after")
150
+
151
+ if self.pdflatex_xelatex == "xelatex":
152
+ data_list = insert_list_in_list(data_list, ["\n\\def\\cn{}\n"], r"\\documentclass", "after")
153
+
154
+ # for bib
155
+ expected = r'\addbibresource{./bibs/abbr.bib}'
156
+ for i in range(len(data_list)):
157
+ mch = expected == data_list[i].rstrip()
158
+ if not mch:
159
+ continue
160
+ if bib_folder_name:
161
+ data_list[i] = "\\addbibresource{" + f"./{bib_folder_name}/{bib_name}" + "}\n"
162
+ else:
163
+ data_list[i] = "\\addbibresource{" + f"./{bib_name}" + "}\n"
164
+
165
+ # body
166
+ if len(data_list_body) != 0:
167
+ data_list.append("\n")
168
+ data_list.extend(data_list_body)
169
+ data_list.append("\n")
170
+
171
+ # tail
172
+ data_list.extend(template_t)
173
+
174
+ # save file
175
+ write_list(data_list, main_name, "w", path_output, False)
176
+ else:
177
+ data_list = read_list(output_tex_name, "r", os.path.join(path_output, tex_folder_name))
178
+ write_list(data_list, main_name, "w", path_output, False)
179
+
180
+ # run latex
181
+ if self.run_latex:
182
+ if shutil.which("latexmk"):
183
+ os.chdir(path_output)
184
+ cmd = f"latexmk -{self.pdflatex_xelatex} {main_name}"
185
+ try:
186
+ subprocess.run(cmd.split(), check=True, capture_output=True, text=True)
187
+ except subprocess.CalledProcessError as e:
188
+ print("Error in Run LaTex:", e.stderr)
189
+ else:
190
+ print("latexmk not found. Please install Texlive.")
191
+
192
+ # delete cache
193
+ if self.delete_run_latex_cache:
194
+ if self.latex_clean_file_types is not None:
195
+ postfix = self.latex_clean_file_types
196
+ else:
197
+ postfix = [".aux", ".bbl", ".bcf", ".blg", ".fdb_latexmk", ".fls", ".log", ".out", ".run.xml"]
198
+ postfix.extend([".synctex.gz", ".gz", ".nav", ".snm", ".toc", ".xdv"])
199
+ delete_files(path_output, postfix)
200
+ return None
pyeasyphd/pyeasyphd.py ADDED
@@ -0,0 +1,86 @@
1
+ import os
2
+
3
+ import sublime
4
+ import sublime_plugin
5
+ from pyadvtools import IterateUpdateDict
6
+
7
+ from pyeasyphd.tools.py_run_bib_md_tex import PyRunBibMdTex
8
+
9
+
10
+ def delete_files(path_storage: str, extensions) -> None:
11
+ """Delete files with specified extensions from storage path.
12
+
13
+ Args:
14
+ path_storage (str): Path to the storage directory.
15
+ extensions: list of file extensions to delete.
16
+ """
17
+ for name in os.listdir(path_storage):
18
+ for ext in extensions:
19
+ if name.endswith(ext) and os.path.isfile(os.path.join(path_storage, name)):
20
+ os.remove(os.path.join(path_storage, name))
21
+
22
+
23
+ class PypapersCommand(sublime_plugin.WindowCommand):
24
+ """Sublime Text command for processing papers with various templates."""
25
+
26
+ def run(self, template="Paper", output_level="next", delete_cache=False):
27
+ """Run the paper processing command.
28
+
29
+ Args:
30
+ template (str, optional): Template type to use. Defaults to "Paper".
31
+ output_level (str, optional): Output level for processing. Defaults to "next".
32
+ delete_cache (bool, optional): Whether to delete cache files. Defaults to False.
33
+ """
34
+ vars_dict = self.window.extract_variables()
35
+
36
+ packages_path = vars_dict["packages"]
37
+
38
+ # settings
39
+ options, default_settings, user_settings, project_settings = {}, {}, {}, {}
40
+ file_default_settings = os.path.join(packages_path, "pypapers", "pypapers.sublime-settings")
41
+ if os.path.exists(file_default_settings):
42
+ default_settings = sublime.decode_value(open(file_default_settings).read())
43
+
44
+ file_user_settings = os.path.join(packages_path, "User", "PyPapers.sublime-settings")
45
+ if os.path.exists(file_user_settings):
46
+ user_settings = sublime.decode_value(open(file_user_settings).read())
47
+
48
+ project_settings = self.window.project_data().get("settings", {})
49
+
50
+ iter_update_dict = IterateUpdateDict()
51
+ options = iter_update_dict.dict_update(options, default_settings)
52
+ options = iter_update_dict.dict_update(options, user_settings)
53
+ options = iter_update_dict.dict_update(options, project_settings)
54
+
55
+ # update
56
+ for key in vars_dict:
57
+ if isinstance(vars_dict[key], str):
58
+ os.environ[key] = vars_dict[key]
59
+
60
+ for key in options:
61
+ if isinstance(options[key], str):
62
+ options[key] = os.path.expandvars(os.path.expanduser(options[key]))
63
+
64
+ if delete_cache:
65
+ file_path = vars_dict["file_path"]
66
+
67
+ if latex_clean_file_types := options.get("latex_clean_file_types", []):
68
+ postfix = latex_clean_file_types
69
+ else:
70
+ postfix = [".aux", ".bbl", ".bcf", ".blg", ".fdb_latexmk", ".fls", ".log", ".out", ".run.xml"]
71
+ postfix.extend([".synctex.gz", ".gz", ".nav", ".snm", ".toc", ".xdv"])
72
+
73
+ delete_files(file_path, postfix)
74
+ delete_files(os.path.dirname(file_path), postfix)
75
+
76
+ else:
77
+ # main
78
+ path_output = options.get("path_output", "")
79
+ if len(path_output.strip()) == 0:
80
+ path_output = vars_dict["file_path"]
81
+
82
+ p_r_l_m = PyRunBibMdTex(path_output, vars_dict["file_extension"], template, options)
83
+ p_r_l_m.run_files([vars_dict["file"]], vars_dict["file_base_name"], output_level)
84
+
85
+ # display
86
+ self.window.status_message("Successful.")