article-introduction-generator 0.1.0__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.
@@ -0,0 +1,624 @@
1
+ import json
2
+ import sys
3
+ from PyQt5.QtWidgets import (
4
+ QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
5
+ QLabel, QTextEdit, QLineEdit, QPushButton, QFileDialog,
6
+ QTabWidget, QListWidget, QMessageBox, QStatusBar, QToolBar,
7
+ QComboBox, QScrollArea, QListWidgetItem
8
+ )
9
+ from PyQt5.QtCore import Qt
10
+
11
+
12
+ # ---------- Reusable Widgets ----------
13
+
14
+ class LabeledTextEdit(QWidget):
15
+ def __init__(self, label, tooltip=""):
16
+ super().__init__()
17
+ layout = QHBoxLayout()
18
+ self.label = QLabel(label)
19
+ self.text = QTextEdit()
20
+ self.text.setToolTip(tooltip)
21
+ self.text.setMinimumHeight(80)
22
+ layout.addWidget(self.label, 1)
23
+ layout.addWidget(self.text, 4)
24
+ self.setLayout(layout)
25
+
26
+ def get(self):
27
+ return self.text.toPlainText().strip()
28
+
29
+ def set(self, value):
30
+ self.text.setPlainText(value or "")
31
+
32
+
33
+ class LabeledLineEdit(QWidget):
34
+ def __init__(self, label, tooltip=""):
35
+ super().__init__()
36
+ layout = QHBoxLayout()
37
+ self.label = QLabel(label)
38
+ self.line = QLineEdit()
39
+ self.line.setToolTip(tooltip)
40
+ layout.addWidget(self.label, 1)
41
+ layout.addWidget(self.line, 4)
42
+ self.setLayout(layout)
43
+
44
+ def get(self):
45
+ return self.line.text().strip()
46
+
47
+ def set(self, value):
48
+ self.line.setText(value or "")
49
+
50
+
51
+ class StringListEditor(QWidget):
52
+ def __init__(self, label, tooltip=""):
53
+ super().__init__()
54
+ layout = QVBoxLayout()
55
+ title = QLabel(label)
56
+ title.setToolTip(tooltip)
57
+ self.list = QListWidget()
58
+
59
+ placeholder = QListWidgetItem("Click 'Add' to insert a new entry")
60
+ placeholder.setFlags(Qt.NoItemFlags) # não selecionável / não editável
61
+ placeholder.setForeground(Qt.gray)
62
+ self.list.addItem(placeholder)
63
+
64
+ self.list.setToolTip(tooltip)
65
+
66
+ btn_layout = QHBoxLayout()
67
+ add_btn = QPushButton("Add")
68
+ remove_btn = QPushButton("Remove")
69
+ btn_layout.addWidget(add_btn)
70
+ btn_layout.addWidget(remove_btn)
71
+
72
+ add_btn.clicked.connect(self.add_item)
73
+ remove_btn.clicked.connect(self.remove_item)
74
+
75
+ layout.addWidget(title)
76
+ layout.addWidget(self.list)
77
+ layout.addLayout(btn_layout)
78
+ self.setLayout(layout)
79
+
80
+ self.list.setEditTriggers(
81
+ self.list.DoubleClicked | self.list.SelectedClicked
82
+ )
83
+
84
+ def add_item(self, text=None):
85
+ from PyQt5.QtWidgets import QListWidgetItem
86
+
87
+ # Se o primeiro item for placeholder, remove
88
+ if self.list.count() == 1:
89
+ item0 = self.list.item(0)
90
+ if item0.flags() == Qt.NoItemFlags:
91
+ self.list.clear()
92
+
93
+
94
+ if not isinstance(text, str):
95
+ text = ""
96
+
97
+ item = QListWidgetItem(text)
98
+ item.setFlags(item.flags() | Qt.ItemIsEditable)
99
+ self.list.addItem(item)
100
+ self.list.editItem(item)
101
+
102
+
103
+ def remove_item(self):
104
+ for item in self.list.selectedItems():
105
+ self.list.takeItem(self.list.row(item))
106
+
107
+ if self.list.count() == 0:
108
+ self._add_placeholder()
109
+
110
+
111
+ def get(self):
112
+ values = []
113
+ for i in range(self.list.count()):
114
+ item = self.list.item(i)
115
+ if not (item.flags() & Qt.ItemIsEditable):
116
+ continue
117
+ text = item.text().strip()
118
+ if text:
119
+ values.append(text)
120
+ return values
121
+
122
+
123
+ def set(self, values):
124
+ self.list.clear()
125
+
126
+ if not values:
127
+ self._add_placeholder()
128
+ return
129
+
130
+ for v in values:
131
+ item = QListWidgetItem(v)
132
+ item.setFlags(item.flags() | Qt.ItemIsEditable)
133
+ self.list.addItem(item)
134
+
135
+
136
+ def _add_placeholder(self):
137
+ placeholder = QListWidgetItem("Click 'Add' to insert a new entry")
138
+ placeholder.setFlags(Qt.NoItemFlags)
139
+ placeholder.setForeground(Qt.gray)
140
+ self.list.addItem(placeholder)
141
+
142
+
143
+
144
+ # ---------- Main Window ----------
145
+
146
+ class JsonIntroductionEditor(QMainWindow):
147
+ def __init__(self):
148
+ super().__init__()
149
+ self.setWindowTitle("Q1 Introduction JSON Editor")
150
+ self.resize(1100, 800)
151
+
152
+ self.current_path = None
153
+
154
+ self.references_data = {}
155
+
156
+ self.current_reference_key = None
157
+
158
+ self.tabs = QTabWidget()
159
+ self.setCentralWidget(self.tabs)
160
+
161
+ self._create_toolbar()
162
+ self._create_status_bar()
163
+
164
+ self._create_tabs()
165
+ self._apply_styles()
166
+
167
+
168
+ # ---------- UI ----------
169
+ def _apply_styles(self):
170
+ self.setStyleSheet("""
171
+ QListWidget {
172
+ border: 1px solid #999;
173
+ border-radius: 4px;
174
+ background-color: #f9f9f9;
175
+ }
176
+
177
+ QListWidget::item {
178
+ padding: 4px;
179
+ }
180
+
181
+ QListWidget::item:selected {
182
+ background-color: #cce5ff;
183
+ color: black;
184
+ }
185
+
186
+ QTextEdit {
187
+ border: 1px solid #bbb;
188
+ border-radius: 4px;
189
+ background-color: #ffffff;
190
+ }
191
+
192
+ QLineEdit {
193
+ border: 1px solid #bbb;
194
+ border-radius: 4px;
195
+ background-color: #ffffff;
196
+ padding: 2px;
197
+ }
198
+ QListWidget:empty {
199
+ background-color: #f9f9f9;
200
+ }
201
+
202
+ QListWidget:empty::item {
203
+ color: #999;
204
+ }
205
+ """)
206
+
207
+ def _create_toolbar(self):
208
+ toolbar = QToolBar("Main Toolbar")
209
+ self.addToolBar(toolbar)
210
+
211
+ load_btn = QPushButton("Load JSON")
212
+ save_btn = QPushButton("Save JSON")
213
+
214
+ load_btn.clicked.connect(self.load_json)
215
+ save_btn.clicked.connect(self.save_json)
216
+
217
+ toolbar.addWidget(load_btn)
218
+ toolbar.addWidget(save_btn)
219
+
220
+ def _create_status_bar(self):
221
+ self.status = QStatusBar()
222
+ self.setStatusBar(self.status)
223
+
224
+ def _wrap_scroll(self, widget):
225
+ scroll = QScrollArea()
226
+ scroll.setWidgetResizable(True)
227
+ scroll.setWidget(widget)
228
+ return scroll
229
+
230
+ def _create_tabs(self):
231
+ self.tabs.addTab(self._wrap_scroll(self._paper_profile_tab()), "Paper Profile")
232
+ self.tabs.addTab(self._wrap_scroll(self._research_problem_tab()), "Research Problem")
233
+ self.tabs.addTab(self._wrap_scroll(self._contributions_tab()), "Contributions")
234
+ self.tabs.addTab(self._related_work_tab(), "Related Work")
235
+ self.tabs.addTab(self._wrap_scroll(self._writing_guidelines_tab()), "Writing Guidelines")
236
+
237
+ # ---------- Tabs ----------
238
+
239
+ def _paper_profile_tab(self):
240
+ w = QWidget()
241
+ layout = QVBoxLayout()
242
+
243
+ self.pp_title = LabeledLineEdit(
244
+ "Title",
245
+ "Full paper title."
246
+ )
247
+ self.pp_domain = LabeledLineEdit(
248
+ "Domain",
249
+ "Research domain, e.g., Computer Vision, NLP, Systems."
250
+ )
251
+ self.pp_journal = LabeledLineEdit(
252
+ "Target Journal",
253
+ "Intended journal (IEEE, Elsevier, ACM, etc.)."
254
+ )
255
+ self.pp_keywords = StringListEditor(
256
+ "Keywords",
257
+ "High-level keywords describing the paper."
258
+ )
259
+ self.pp_summary = LabeledTextEdit(
260
+ "Author Intended Summary",
261
+ "Human-written summary describing what the paper does and why."
262
+ )
263
+
264
+ for wdg in [self.pp_title, self.pp_domain, self.pp_journal, self.pp_keywords, self.pp_summary]:
265
+ layout.addWidget(wdg)
266
+
267
+ w.setLayout(layout)
268
+ return w
269
+
270
+ def _research_problem_tab(self):
271
+ w = QWidget()
272
+ layout = QVBoxLayout()
273
+
274
+ self.rp_overview = LabeledTextEdit(
275
+ "Research Domain Overview",
276
+ "General overview of the research domain and its importance."
277
+ )
278
+ self.rp_specific = LabeledTextEdit(
279
+ "Specific Problem",
280
+ "Precise formulation of the problem addressed."
281
+ )
282
+ self.rp_challenges = StringListEditor(
283
+ "Practical Challenges",
284
+ "Key practical or theoretical challenges."
285
+ )
286
+ self.rp_insufficient = LabeledTextEdit(
287
+ "Why Existing Solutions Are Insufficient",
288
+ "High-level human assessment without citing specific papers."
289
+ )
290
+
291
+ for wdg in [self.rp_overview, self.rp_specific, self.rp_challenges, self.rp_insufficient]:
292
+ layout.addWidget(wdg)
293
+
294
+ w.setLayout(layout)
295
+ return w
296
+
297
+ def _contributions_tab(self):
298
+ w = QWidget()
299
+ layout = QVBoxLayout()
300
+
301
+ self.contributions = StringListEditor(
302
+ "Contributions",
303
+ "Main contributions of the paper, one per entry."
304
+ )
305
+
306
+ layout.addWidget(self.contributions)
307
+ w.setLayout(layout)
308
+ return w
309
+
310
+ def _related_work_tab(self):
311
+ tabs = QTabWidget()
312
+ tabs.addTab(self._references_tab(), "References")
313
+ tabs.addTab(self._wrap_scroll(self._synthesis_tab()), "Human Curated Synthesis")
314
+ return tabs
315
+
316
+ def _references_tab(self):
317
+ w = QWidget()
318
+ layout = QHBoxLayout()
319
+
320
+ self.ref_list = QListWidget()
321
+ self.ref_list.setEditTriggers(
322
+ QListWidget.DoubleClicked | QListWidget.EditKeyPressed
323
+ )
324
+ self.ref_list.itemChanged.connect(self._on_reference_renamed)
325
+ self.ref_list.currentItemChanged.connect(self._load_reference)
326
+
327
+ # ---- Right panel (scrollable) ----
328
+ right_widget = QWidget()
329
+ right = QVBoxLayout()
330
+ right_widget.setLayout(right)
331
+
332
+ self.ref_bibtex = LabeledTextEdit("BibTeX", "BibTeX entry. This is the only source for citations.")
333
+ self.ref_abstract = LabeledTextEdit("Abstract", "Original abstract of the cited paper.")
334
+ self.ref_category = LabeledLineEdit("Methodological Category", "e.g., deep_learning, transformer_based, graph_based.")
335
+ self.ref_contribution = LabeledTextEdit("Central Technical Contribution", "Main technical idea introduced by this work.")
336
+ self.ref_strengths = StringListEditor("Author Reported Strengths", "Strengths explicitly claimed by the original authors.")
337
+ self.ref_limitations = StringListEditor("Reported Limitations", "Limitations discussed or implied by the paper.")
338
+ self.ref_relevance = LabeledTextEdit("Relevance to Our Work", "How this work relates to and differs from our paper.")
339
+ self.ref_role = LabeledLineEdit("Introduction Paragraph Role", "foundational, early_state_of_art, recent_advances, etc.")
340
+
341
+ for wdg in [
342
+ self.ref_bibtex, self.ref_abstract, self.ref_category,
343
+ self.ref_contribution, self.ref_strengths,
344
+ self.ref_limitations, self.ref_relevance, self.ref_role
345
+ ]:
346
+ right.addWidget(wdg)
347
+
348
+ btns = QHBoxLayout()
349
+ add = QPushButton("Add Reference")
350
+ remove = QPushButton("Remove Reference")
351
+ btns.addWidget(add)
352
+ btns.addWidget(remove)
353
+
354
+ add.clicked.connect(self._add_reference)
355
+ remove.clicked.connect(self._remove_reference)
356
+
357
+ right.addLayout(btns)
358
+
359
+ scroll = QScrollArea()
360
+ scroll.setWidgetResizable(True)
361
+ scroll.setWidget(right_widget)
362
+
363
+ layout.addWidget(self.ref_list, 1)
364
+ layout.addWidget(scroll, 3)
365
+
366
+ w.setLayout(layout)
367
+ return w
368
+
369
+ def _on_reference_renamed(self, item: QListWidgetItem):
370
+ old_key = self.current_reference_key
371
+ new_key = item.text().strip()
372
+
373
+ if not old_key:
374
+ return
375
+
376
+ if not new_key:
377
+ QMessageBox.warning(self, "Invalid name", "Reference key cannot be empty.")
378
+ self.ref_list.blockSignals(True)
379
+ item.setText(old_key)
380
+ self.ref_list.blockSignals(False)
381
+ return
382
+
383
+ if new_key in self.references_data and new_key != old_key:
384
+ QMessageBox.warning(self, "Duplicate key", "This reference key already exists.")
385
+ self.ref_list.blockSignals(True)
386
+ item.setText(old_key)
387
+ self.ref_list.blockSignals(False)
388
+ return
389
+
390
+ self._save_current_reference()
391
+
392
+ self.references_data[new_key] = self.references_data.pop(old_key)
393
+ self.current_reference_key = new_key
394
+
395
+
396
+ def _synthesis_tab(self):
397
+ w = QWidget()
398
+ layout = QVBoxLayout()
399
+
400
+ self.syn_trends = StringListEditor(
401
+ "Common Trends",
402
+ "Observed trends across the literature."
403
+ )
404
+ self.syn_open = StringListEditor(
405
+ "Open Problems",
406
+ "Unresolved problems identified by the author."
407
+ )
408
+ self.syn_gap = LabeledTextEdit(
409
+ "Explicit Research Gap",
410
+ "Clear formulation of the research gap addressed by the paper."
411
+ )
412
+
413
+ for wdg in [self.syn_trends, self.syn_open, self.syn_gap]:
414
+ layout.addWidget(wdg)
415
+
416
+ w.setLayout(layout)
417
+ return w
418
+
419
+ def _writing_guidelines_tab(self):
420
+ w = QWidget()
421
+ layout = QVBoxLayout()
422
+
423
+ self.wg = LabeledTextEdit(
424
+ "Writing Guidelines",
425
+ "Explicit instructions to be followed by the LLM when generating text."
426
+ )
427
+
428
+ layout.addWidget(self.wg)
429
+ w.setLayout(layout)
430
+ return w
431
+
432
+ # ---------- Reference Helpers ----------
433
+
434
+ def _add_reference(self):
435
+ if self.references_data:
436
+ indices = []
437
+ for k in self.references_data.keys():
438
+ try:
439
+ indices.append(int(k.split("_")[1]))
440
+ except (IndexError, ValueError):
441
+ pass
442
+ next_idx = max(indices) + 1 if indices else 1
443
+ else:
444
+ next_idx = 1
445
+
446
+ key = f"ref_{next_idx}"
447
+ self.references_data[key] = {}
448
+
449
+ self.ref_list.blockSignals(True)
450
+
451
+ item = QListWidgetItem(key)
452
+ item.setFlags(item.flags() | Qt.ItemIsEditable)
453
+ self.ref_list.addItem(item)
454
+ self.ref_list.setCurrentItem(item)
455
+
456
+ self.current_reference_key = key
457
+
458
+ self.ref_list.blockSignals(False)
459
+
460
+ self._clear_reference_editor()
461
+
462
+ def _clear_reference_editor(self):
463
+ self.ref_bibtex.set("")
464
+ self.ref_abstract.set("")
465
+ self.ref_category.set("")
466
+ self.ref_contribution.set("")
467
+ self.ref_strengths.set([])
468
+ self.ref_limitations.set([])
469
+ self.ref_relevance.set("")
470
+ self.ref_role.set("")
471
+
472
+ def _remove_reference(self):
473
+ for item in self.ref_list.selectedItems():
474
+ key = item.text()
475
+ self.references_data.pop(key, None)
476
+ self.ref_list.takeItem(self.ref_list.row(item))
477
+
478
+ if key == self.current_reference_key:
479
+ self.current_reference_key = None
480
+
481
+ self._clear_reference_editor()
482
+
483
+ def _save_current_reference(self):
484
+ if not self.current_reference_key:
485
+ return
486
+
487
+ self.references_data[self.current_reference_key] = {
488
+ "bibtex": self.ref_bibtex.get(),
489
+ "abstract": self.ref_abstract.get(),
490
+ "methodological_category": self.ref_category.get(),
491
+ "central_technical_contribution": self.ref_contribution.get(),
492
+ "author_reported_strengths": self.ref_strengths.get(),
493
+ "reported_limitations": self.ref_limitations.get(),
494
+ "relevance_to_our_work": self.ref_relevance.get(),
495
+ "introduction_paragraph_role": self.ref_role.get(),
496
+ }
497
+
498
+
499
+ def _load_reference(self, current, previous):
500
+ if previous and self.current_reference_key:
501
+ self._save_current_reference()
502
+
503
+ if not current:
504
+ self.current_reference_key = None
505
+ return
506
+
507
+ self.ref_list.blockSignals(True)
508
+
509
+ self.current_reference_key = current.text()
510
+ ref = self.references_data.get(self.current_reference_key, {})
511
+
512
+ self.ref_bibtex.set(ref.get("bibtex"))
513
+ self.ref_abstract.set(ref.get("abstract"))
514
+ self.ref_category.set(ref.get("methodological_category"))
515
+ self.ref_contribution.set(ref.get("central_technical_contribution"))
516
+ self.ref_strengths.set(ref.get("author_reported_strengths", []))
517
+ self.ref_limitations.set(ref.get("reported_limitations", []))
518
+ self.ref_relevance.set(ref.get("relevance_to_our_work"))
519
+ self.ref_role.set(ref.get("introduction_paragraph_role"))
520
+
521
+ self.ref_list.blockSignals(False)
522
+
523
+
524
+ # ---------- Load / Save ----------
525
+
526
+ def load_json(self):
527
+ self._save_current_reference()
528
+
529
+ path, _ = QFileDialog.getOpenFileName(self, "Load JSON", "", "JSON Files (*.json)")
530
+ if not path:
531
+ return
532
+
533
+ with open(path, "r", encoding="utf-8") as f:
534
+ data = json.load(f)
535
+
536
+ self.current_path = path
537
+ self.status.showMessage(f"Loaded from {path}")
538
+
539
+ # ---- Paper Profile ----
540
+ pp = data.get("paper_profile", {})
541
+ self.pp_title.set(pp.get("title"))
542
+ self.pp_domain.set(pp.get("domain"))
543
+ self.pp_journal.set(pp.get("target_journal"))
544
+ self.pp_keywords.set(pp.get("keywords", []))
545
+ self.pp_summary.set(pp.get("author_intended_summary"))
546
+
547
+ # ---- Research Problem ----
548
+ rp = data.get("research_problem", {})
549
+ self.rp_overview.set(rp.get("research_domain_overview"))
550
+ self.rp_specific.set(rp.get("specific_problem"))
551
+ self.rp_challenges.set(rp.get("practical_challenges", []))
552
+ self.rp_insufficient.set(rp.get("why_existing_solutions_are_insufficient"))
553
+
554
+ # ---- Contributions ----
555
+ self.contributions.set(data.get("contributions", []))
556
+
557
+ # ---- Writing Guidelines ----
558
+ self.wg.set(data.get("writing_guidelines", ""))
559
+
560
+ # ---- Related Work: References ----
561
+ self.references_data = data.get("related_work", {}).get("references", {})
562
+ self.ref_list.clear()
563
+
564
+ for key in self.references_data.keys():
565
+ item = QListWidgetItem(key)
566
+ item.setFlags(item.flags() | Qt.ItemIsEditable)
567
+ self.ref_list.addItem(item)
568
+
569
+ # ---- Related Work: Human Curated Synthesis ----
570
+ synth = data.get("related_work", {}).get("human_curated_synthesis", {})
571
+ self.syn_trends.set(synth.get("common_trends", []))
572
+ self.syn_open.set(synth.get("open_problems", []))
573
+ self.syn_gap.set(synth.get("explicit_research_gap"))
574
+
575
+ self.current_reference_key = None
576
+ self._clear_reference_editor()
577
+
578
+ def save_json(self):
579
+ self._save_current_reference()
580
+
581
+ path, _ = QFileDialog.getSaveFileName(self, "Save JSON", "", "JSON Files (*.json)")
582
+ if not path:
583
+ return
584
+
585
+ data = {
586
+ "paper_profile": {
587
+ "title": self.pp_title.get(),
588
+ "domain": self.pp_domain.get(),
589
+ "target_journal": self.pp_journal.get(),
590
+ "keywords": self.pp_keywords.get(),
591
+ "author_intended_summary": self.pp_summary.get()
592
+ },
593
+ "research_problem": {
594
+ "research_domain_overview": self.rp_overview.get(),
595
+ "specific_problem": self.rp_specific.get(),
596
+ "practical_challenges": self.rp_challenges.get(),
597
+ "why_existing_solutions_are_insufficient": self.rp_insufficient.get()
598
+ },
599
+ "contributions": self.contributions.get(),
600
+ "related_work": {
601
+ "references": self.references_data,
602
+ "human_curated_synthesis": {
603
+ "common_trends": self.syn_trends.get(),
604
+ "open_problems": self.syn_open.get(),
605
+ "explicit_research_gap": self.syn_gap.get()
606
+ }
607
+ },
608
+ "writing_guidelines": self.wg.get()
609
+ }
610
+
611
+ with open(path, "w", encoding="utf-8") as f:
612
+ json.dump(data, f, indent=2)
613
+
614
+ self.current_path = path
615
+ self.status.showMessage(f"Saved to {path}")
616
+
617
+
618
+ # ---------- Main ----------
619
+
620
+ if __name__ == "__main__":
621
+ app = QApplication(sys.argv)
622
+ win = JsonIntroductionEditor()
623
+ win.show()
624
+ sys.exit(app.exec_())
@@ -0,0 +1,59 @@
1
+ Metadata-Version: 2.4
2
+ Name: article_introduction_generator
3
+ Version: 0.1.0
4
+ Summary: Program to generate an introduction from a JSON File
5
+ Author-email: Fernando Pujaico Rivera <fernando.pujaico.rivera@gmail.com>
6
+ Maintainer-email: Fernando Pujaico Rivera <fernando.pujaico.rivera@gmail.com>
7
+ License-Expression: GPL-3.0-only WITH Classpath-exception-2.0 OR BSD-3-Clause
8
+ Project-URL: Bug Reports, https://github.com/trucomanx/ArticleIntroductionGenerator/issues
9
+ Project-URL: Funding, https://trucomanx.github.io/en/funding.html
10
+ Project-URL: Documentation, https://github.com/trucomanx/ArticleIntroductionGenerator/tree/main/doc
11
+ Project-URL: Source, https://github.com/trucomanx/ArticleIntroductionGenerator
12
+ Keywords: writing,article
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: PyQt5
19
+ Requires-Dist: deep-consultation
20
+ Dynamic: license-file
21
+
22
+ # article-introduction-generator
23
+
24
+ Program to generate an introduction from a JSON File
25
+
26
+ ![logo](https://raw.githubusercontent.com/trucomanx/ArticleIntroductionGenerator/main/screenshot.png)
27
+
28
+ ## 1. Installing
29
+
30
+ To install the package from [PyPI](https://pypi.org/project/article_introduction_generator/), follow the instructions below:
31
+
32
+
33
+ ```bash
34
+ pip install --upgrade article_introduction_generator
35
+ ```
36
+
37
+ Execute `which article-introduction-generator` to see where it was installed, probably in `/home/USERNAME/.local/bin/article-introduction-generator`.
38
+
39
+ ### Using
40
+
41
+ To start, use the command below:
42
+
43
+ ```bash
44
+ article-introduction-generator
45
+ ```
46
+ ## 2. More information
47
+
48
+ If you want more information go to [doc](https://github.com/trucomanx/ArticleIntroductionGenerator/blob/main/doc) directory.
49
+
50
+ ## 3. Buy me a coffee
51
+
52
+ If you find this tool useful and would like to support its development, you can buy me a coffee!
53
+ Your donations help keep the project running and improve future updates.
54
+
55
+ [☕ Buy me a coffee](https://ko-fi.com/trucomanx)
56
+
57
+ ## 4. License
58
+
59
+ This project is licensed under the GPL license. See the `LICENSE` file for more details.
@@ -0,0 +1,16 @@
1
+ article_introduction_generator/__init__.py,sha256=ruULPQI7A5xxFhCYlc2-Yak7eGnabqCYTahYYvxbPtc,19
2
+ article_introduction_generator/about.py,sha256=AcHtUHiefZ0oxi2AvSjsR_hCKzh7unEql1g2JjWM9ms,611
3
+ article_introduction_generator/desktop.py,sha256=bpg1TeAzIKDvp5qUOs-1EogeYigir-gd7zPFVisTIZk,3528
4
+ article_introduction_generator/program.py,sha256=ahmioGH_riEgG7VV176vPrG2Mbf6DprKGrylO36qVyc,33863
5
+ article_introduction_generator/program_old.py,sha256=RqORCmqLYvTmIN0mMsXS3A3kJS-llmHXCh-vOxXkQqk,20212
6
+ article_introduction_generator/icons/logo.png,sha256=SE1ckm6RpcoheYDNrhxWPIszQCEwgGwe_xBfzZrVR4c,117312
7
+ article_introduction_generator/modules/__init__.py,sha256=ruULPQI7A5xxFhCYlc2-Yak7eGnabqCYTahYYvxbPtc,19
8
+ article_introduction_generator/modules/configure.py,sha256=K48SPidC_LRDXE2Q5QLGOG2-zPiMHjFgu4gv-3WWnns,1923
9
+ article_introduction_generator/modules/consult.py,sha256=La3Xgj9EVzIluylGggd3pzhkjLjsC-mnSdzC-Q8cOk0,2945
10
+ article_introduction_generator/modules/wabout.py,sha256=a8WOE9FJ9AdmbFioK17lqbR0cWavBSKhTzjrhsMJn10,4455
11
+ article_introduction_generator-0.1.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
12
+ article_introduction_generator-0.1.0.dist-info/METADATA,sha256=-zdxutpQfvGkr2R4zfmhgwCSIPuOUsWsGoHAkSlnxsI,2131
13
+ article_introduction_generator-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
14
+ article_introduction_generator-0.1.0.dist-info/entry_points.txt,sha256=aEZZr87sAlDrTi_6Xw26oKj2cd8uoujgov_TW_XYIqk,95
15
+ article_introduction_generator-0.1.0.dist-info/top_level.txt,sha256=MFgK4ElkA2Af3CwTC-IfN3XivimjUkmaobCF6Itt1o0,31
16
+ article_introduction_generator-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+