387 lines
16 KiB
Python
387 lines
16 KiB
Python
import sys
|
|||
|
|
import re
|
||
|
|
|
||
|
|
with open('composeview.cpp', 'r') as f:
|
||
|
|
lines = f.readlines()
|
||
|
|
|
||
|
|
# 1. Remove AddressInputWidget class definition (lines approx 27-52)
|
||
|
|
# Find start and end lines.
|
||
|
|
new_lines = []
|
||
|
|
i = 0
|
||
|
|
while i < len(lines):
|
||
|
|
if lines[i].strip() == '// Address input widget with tokenized entries, delete button, drag-drop reorder, and autocomplete':
|
||
|
|
# Skip until after the closing brace of the class
|
||
|
|
# Find the line with '};' after the class
|
||
|
|
j = i
|
||
|
|
while j < len(lines) and not lines[j].strip().startswith('};'):
|
||
|
|
j += 1
|
||
|
|
# include the '};' line
|
||
|
|
j += 1
|
||
|
|
i = j
|
||
|
|
continue
|
||
|
|
new_lines.append(lines[i])
|
||
|
|
i += 1
|
||
|
|
|
||
|
|
lines = new_lines
|
||
|
|
|
||
|
|
# 2. Replace member variable types
|
||
|
|
# We'll process line by line, replacing specific patterns.
|
||
|
|
out_lines = []
|
||
|
|
for line in lines:
|
||
|
|
# Replace member declarations
|
||
|
|
if 'AddressInputWidget *m_toWidget = nullptr;' in line:
|
||
|
|
line = line.replace('AddressInputWidget *m_toWidget = nullptr;', 'QLineEdit *m_toField = nullptr;')
|
||
|
|
if 'AddressInputWidget *m_ccWidget = nullptr;' in line:
|
||
|
|
line = line.replace('AddressInputWidget *m_ccWidget = nullptr;', 'QLineEdit *m_ccField = nullptr;')
|
||
|
|
if 'AddressInputWidget *m_bccWidget = nullptr;' in line:
|
||
|
|
line = line.replace('AddressInputWidget *m_bccWidget = nullptr;', 'QLineEdit *m_bccField = nullptr;')
|
||
|
|
# Remove duplicate m_ccField and m_bccField lines (they are later)
|
||
|
|
if 'QLineEdit *m_ccField = nullptr; // kept for compatibility? actually not used now' in line:
|
||
|
|
# skip this line
|
||
|
|
continue
|
||
|
|
if 'QLineEdit *m_bccField = nullptr;' in line and '// kept for compatibility' not in line:
|
||
|
|
# This is the second duplicate; we already have one above; we can keep only one.
|
||
|
|
# We'll skip this line.
|
||
|
|
continue
|
||
|
|
out_lines.append(line)
|
||
|
|
|
||
|
|
lines = out_lines
|
||
|
|
|
||
|
|
# Now we need to modify setAccountService, setupUI, slots, etc.
|
||
|
|
# We'll do a more aggressive approach: replace entire sections using markers.
|
||
|
|
# Let's join back to string and use regex.
|
||
|
|
|
||
|
|
text = ''.join(lines)
|
||
|
|
|
||
|
|
# Replace setAccountService function
|
||
|
|
pattern_setAccountService = re.compile(r'void ComposeView::setAccountService\(AccountService \*service\)\s*\{[\s\S]*?\}', re.MULTILINE)
|
||
|
|
def repl_setAccountService(match):
|
||
|
|
return '''void ComposeView::setAccountService(AccountService *service)
|
||
|
|
{
|
||
|
|
m_accountService = service;
|
||
|
|
populateAccountCombo();
|
||
|
|
// Update completer models for address widgets with known emails
|
||
|
|
if (m_accountService) {
|
||
|
|
QStringList knownEmails;
|
||
|
|
const QVector<Account> accounts = m_accountService->getAllAccounts();
|
||
|
|
for (const Account &acc : accounts) {
|
||
|
|
knownEmails << acc.email();
|
||
|
|
}
|
||
|
|
// Set completion model for each widget
|
||
|
|
QStringListModel *model = new QStringListModel(knownEmails, this);
|
||
|
|
QCompleter *comp = new QCompleter(model, this);
|
||
|
|
comp->setCaseSensitivity(Qt::CaseInsensitive);
|
||
|
|
if (m_toField) m_toField->setCompleter(comp);
|
||
|
|
// For cc and bcc we need separate completers but can share model
|
||
|
|
QCompleter *comp2 = new QCompleter(model, this);
|
||
|
|
comp2->setCaseSensitivity(Qt::CaseInsensitive);
|
||
|
|
if (m_ccField) m_ccField->setCompleter(comp2);
|
||
|
|
QCompleter *comp3 = new QCompleter(model, this);
|
||
|
|
comp3->setCaseSensitivity(Qt::CaseInsensitive);
|
||
|
|
if (m_bccField) m_bccField->setCompleter(comp3);
|
||
|
|
}
|
||
|
|
}'''
|
||
|
|
text = re.sub(pattern_setAccountService, repl_setAccountService, text)
|
||
|
|
|
||
|
|
# Replace setupCore function: we need to replace the whole function.
|
||
|
|
# We'll rewrite from scratch using a marker: we will replace the whole function body.
|
||
|
|
# Let's capture the function and replace.
|
||
|
|
pattern_setupUI = re.compile(r'void ComposeView::setupUI\(\)\s*\{[\s\S]*?\n\}\s*\n(?=void|\Z)', re.MULTILINE)
|
||
|
|
def repl_setupUI(match):
|
||
|
|
return '''void ComposeView::setupUI()
|
||
|
|
{
|
||
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||
|
|
mainLayout->setContentsMargins(20,15,20,15);
|
||
|
|
mainLayout->setSpacing(8);
|
||
|
|
|
||
|
|
// Top row: Account selector and detach button
|
||
|
|
QHBoxLayout *topLayout = new QHBoxLayout();
|
||
|
|
m_accountLabel = new QLabel(tr(\"From:\"), this);
|
||
|
|
m_accountLabel->setFixedWidth(40);
|
||
|
|
m_accountLabel->setStyleSheet(QStringLiteral(\"font-weight: bold; color: #555;\"));
|
||
|
|
m_accountCombo = new QComboBox(this);
|
||
|
|
m_accountCombo->setFixedWidth(180);
|
||
|
|
m_accountCombo->setStyleSheet(
|
||
|
|
\"QComboBox { border: 1px solid #d1d1d6; border-radius: 4px; padding: 6px 8px; \"
|
||
|
|
\"background: white; min-height: 20px; }\"\n
|
||
|
|
\"QComboBox:hover { border-color: #bdbdbd; }\"\n
|
||
|
|
\"QComboBox:focus { border-color: #1976D2; }\"
|
||
|
|
);
|
||
|
|
m_detachButton = new QPushButton(this);
|
||
|
|
m_detachButton->setToolTip(tr(\"Detach compose window\"));
|
||
|
|
m_detachButton->setText(QStringLiteral(\"⤢\"));
|
||
|
|
m_detachButton->setFixedSize(28,28);
|
||
|
|
m_detachButton->setStyleSheet(
|
||
|
|
\"QPushButton { background: transparent; border: 1px solid #d1d1d6; border-radius: 4px; \"
|
||
|
|
\"padding: 6px; color: #555; font-size: 14px; }\"\n
|
||
|
|
\"QPushButton:hover { background: #f0f0f0; }\"\n
|
||
|
|
);
|
||
|
|
connect(m_detachButton, &QPushButton::clicked, this, &ComposeView::onDetachClicked);
|
||
|
|
|
||
|
|
topLayout->addWidget(m_accountLabel);
|
||
|
|
topLayout->addWidget(m_accountCombo, 1);
|
||
|
|
topLayout->addWidget(m_detachButton);
|
||
|
|
mainLayout->addLayout(topLayout);
|
||
|
|
|
||
|
|
// Separator line
|
||
|
|
QFrame *line1 = new QFrame(this);
|
||
|
|
line1->setFrameShape(QFrame::HLine);
|
||
|
|
line1->setStyleSheet(QStringLiteral(\"color: #e0e0e0;\"));
|
||
|
|
mainLayout->addWidget(line1);
|
||
|
|
|
||
|
|
// To row
|
||
|
|
QHBoxLayout *toLayout = new QHBoxLayout();
|
||
|
|
QLabel *toLabel = new QLabel(tr(\"To:\"), this);
|
||
|
|
toLabel->setFixedWidth(40);
|
||
|
|
toLabel->setStyleSheet(QStringLiteral(\"font-weight: bold; color: #555;\"));
|
||
|
|
m_toField = new QLineEdit(this);
|
||
|
|
m_toField->setPlaceholderText(tr(\"To recipients\"));
|
||
|
|
toLayout->addWidget(toLabel);
|
||
|
|
toLayout->addWidget(m_toField, 1);
|
||
|
|
mainLayout->addLayout(toLayout);
|
||
|
|
|
||
|
|
// CC/BCC toggle buttons
|
||
|
|
QHBoxLayout *toggleLayout = new QHBoxLayout();
|
||
|
|
m_ccButton = new QPushButton(tr(\"CC\"), this);
|
||
|
|
m_ccButton->setCheckable(true);
|
||
|
|
m_ccButton->setChecked(false);
|
||
|
|
connect(m_ccButton, &QPushButton::toggled, this, &ComposeView::onCcToggled);
|
||
|
|
m_bccButton = new QPushButton(tr(\"BCC\"), this);
|
||
|
|
m_bccButton->setCheckable(true);
|
||
|
|
m_bccButton->setChecked(false);
|
||
|
|
connect(m_bccButton, &QPushButton::toggled, this, &ComposeView::onBccToggled);
|
||
|
|
toggleLayout->addWidget(m_ccButton);
|
||
|
|
toggleLayout->addWidget(m_bccButton);
|
||
|
|
toggleLayout->addStretch();
|
||
|
|
mainLayout->addLayout(toggleLayout);
|
||
|
|
|
||
|
|
// Cc row (hidden by default)
|
||
|
|
m_ccRow = new QWidget(this);
|
||
|
|
QHBoxLayout *ccLayout = new QHBoxLayout(m_ccRow);
|
||
|
|
ccLayout->setContentsMargins(0,0,0,0);
|
||
|
|
QLabel *ccLabel = new QLabel(tr(\"Cc:\"), this);
|
||
|
|
ccLabel->setFixedWidth(40);
|
||
|
|
ccLabel->setStyleSheet(QStringLiteral(\"color: #555;\"));
|
||
|
|
m_ccField = new QLineEdit(this);
|
||
|
|
m_ccField->setPlaceholderText(tr(\"Cc recipients\"));
|
||
|
|
ccLayout->addWidget(ccLabel);
|
||
|
|
ccLayout->addWidget(m_ccField, 1);
|
||
|
|
m_ccRow->setVisible(false);
|
||
|
|
mainLayout->addWidget(m_ccRow);
|
||
|
|
|
||
|
|
// Bcc row (hidden by default)
|
||
|
|
m_bccRow = new QWidget(this);
|
||
|
|
QHBoxLayout *bccLayout = new QHBoxLayout(m_bccRow);
|
||
|
|
bccLayout->setContentsMargins(0,0,0,0);
|
||
|
|
QLabel *bccLabel = new QLabel(tr(\"Bcc:\"), this);
|
||
|
|
bccLabel->setFixedWidth(40);
|
||
|
|
bccLabel->setStyleSheet(QStringLiteral(\"color: #555;\"));
|
||
|
|
m_bccField = new QLineEdit(this);
|
||
|
|
m_bccField->setPlaceholderText(tr(\"Bcc recipients\"));
|
||
|
|
bccLayout->addWidget(bccLabel);
|
||
|
|
bccLayout->addWidget(m_bccField, 1);
|
||
|
|
m_bccRow->setVisible(false);
|
||
|
|
mainLayout->addWidget(m_bccRow);
|
||
|
|
|
||
|
|
// Separator
|
||
|
|
QFrame *line2 = new QFrame(this);
|
||
|
|
line2->setFrameShape(QFrame::HLine);
|
||
|
|
line2->setStyleSheet(QStringLiteral(\"color: #e0e0e0;\"));
|
||
|
|
mainLayout->addWidget(line2);
|
||
|
|
|
||
|
|
// Subject field
|
||
|
|
QLabel *subjectLabel = new QLabel(tr(\"Subject:\"), this);
|
||
|
|
subjectLabel->setFixedWidth(50);
|
||
|
|
subjectLabel->setStyleSheet(QStringLiteral(\"font-weight: bold; color: #555;\"));
|
||
|
|
m_subjectField = new QLineEdit(this);
|
||
|
|
m_subjectField->setPlaceholderText(tr(\"Subject\"));
|
||
|
|
m_subjectField->setFixedHeight(36);
|
||
|
|
QFont subjectFont = m_subjectField->font();
|
||
|
|
subjectFont.setPointSize(14);
|
||
|
|
subjectFont.setBold(true);
|
||
|
|
m_subjectField->setFont(subjectFont);
|
||
|
|
QHBoxLayout *subjectLayout = new QHBoxLayout();
|
||
|
|
subjectLayout->addWidget(subjectLabel);
|
||
|
|
subjectLayout->addWidget(m_subjectField, 1);
|
||
|
|
mainLayout->addLayout(subjectLayout);
|
||
|
|
|
||
|
|
// Body editor with toolbar
|
||
|
|
m_bodyEditor = new RichTextEditor(this);
|
||
|
|
m_bodyEditor->setupToolbar(mainLayout);
|
||
|
|
mainLayout->addWidget(m_bodyEditor, 1);
|
||
|
|
|
||
|
|
// Schedule panel (hidden by default)
|
||
|
|
m_schedulePanel = new QWidget(this);
|
||
|
|
QHBoxLayout *scheduleLayout = new QHBoxLayout(m_schedulePanel);
|
||
|
|
scheduleLayout->setContentsMargins(0,0,0,0);
|
||
|
|
QLabel *scheduleLabel = new QLabel(tr(\"Send at:\"), this);
|
||
|
|
scheduleLabel->setStyleSheet(QStringLiteral(\"color: #555; font-weight: bold;\"));
|
||
|
|
m_schedulePicker = new QDateTimeEdit(QDateTime::currentDateTime().addSecs(3600), this);
|
||
|
|
m_schedulePicker->setCalendarPopup(true);
|
||
|
|
m_schedulePicker->setDisplayFormat(QStringLiteral(\"dd/MM/yyyy hh:mm AP\"));
|
||
|
|
scheduleLayout->addWidget(scheduleLabel);
|
||
|
|
scheduleLayout->addWidget(m_schedulePicker);
|
||
|
|
scheduleLayout->addStretch();
|
||
|
|
m_scheduleSendButton = new QPushButton(tr(\"Schedule Send\"), this);
|
||
|
|
m_scheduleSendButton->setStyleSheet(
|
||
|
|
\"QPushButton { background-color: #1976D2; color: white; border: none; border-radius: 4px; \"
|
||
|
|
\"padding: 6px 16px; font-weight: bold; }\"\n
|
||
|
|
\"QPushButton:hover { background-color: #1565C0; }\"\n
|
||
|
|
);
|
||
|
|
connect(m_scheduleSendButton, &QPushButton::clicked, this, &ComposeView::onScheduleClicked);
|
||
|
|
scheduleLayout->addWidget(m_scheduleSendButton);
|
||
|
|
m_schedulePanel->setVisible(false);
|
||
|
|
mainLayout->addWidget(m_schedulePanel);
|
||
|
|
|
||
|
|
// Action buttons row
|
||
|
|
QHBoxLayout *actionsLayout = new QHBoxLayout();
|
||
|
|
actionsLayout->addStretch();
|
||
|
|
|
||
|
|
m_discardButton = new QPushButton(tr(\"Discard\"), this);
|
||
|
|
m_discardButton->setStyleSheet(
|
||
|
|
\"QPushButton { background: transparent; border: 1px solid #d1d1d6; border-radius: 4px; \"
|
||
|
|
\"padding: 8px 20px; color: #555; }\"\n
|
||
|
|
\"QPushButton:hover { background: #f5f5f5; }\"\n
|
||
|
|
);
|
||
|
|
actionsLayout->addWidget(m_discardButton);
|
||
|
|
connect(m_discardButton, &QPushButton::clicked, this, &ComposeView::discardRequested);
|
||
|
|
|
||
|
|
// Split button for Send / Schedule
|
||
|
|
m_sendMenu = new QMenu(this);
|
||
|
|
m_sendNowAction = m_sendMenu->addAction(tr(\"Send Now\"));
|
||
|
|
connect(m_sendNowAction, &QAction::triggered, this, &ComposeView::onSendClicked);
|
||
|
|
m_scheduleAction = m_sendMenu->addAction(tr(\"Schedule for later...\"));
|
||
|
|
connect(m_scheduleAction, &QAction::triggered, [this]() {
|
||
|
|
m_schedulePanel->setVisible(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
m_sendSplit = new QToolButton(this);
|
||
|
|
m_sendSplit->setText(tr(\"Send\"));
|
||
|
|
m_sendSplit->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
||
|
|
m_sendSplit->setPopupMode(QToolButton::MenuButtonPopup);
|
||
|
|
m_sendSplit->setMenu(m_sendMenu);
|
||
|
|
m_sendSplit->setStyleSheet(
|
||
|
|
\"QToolButton { background-color: #1976D2; color: white; border: none; border-radius: 4px; \"
|
||
|
|
\"padding: 8px 24px; font-weight: bold; }\"\n
|
||
|
|
\"QToolButton:hover { background-color: #1565C0; }\"\n
|
||
|
|
\"QToolButton::menu-button { border-left: 1px solid rgba(255,255,255,0.3); padding-left: 8px; padding-right: 8px; }\"\n
|
||
|
|
\"QToolButton::menu-button:hover { background-color: #1565C0; border-top-right-radius: 4px; border-bottom-right-radius: 4px; }\"\n
|
||
|
|
);
|
||
|
|
connect(m_sendSplit, &QToolButton::clicked, this, &ComposeView::onSendClicked);
|
||
|
|
actionsLayout->addWidget(m_sendSplit);
|
||
|
|
|
||
|
|
mainLayout->addLayout(actionsLayout);
|
||
|
|
|
||
|
|
// Connect rich text editor signature signals
|
||
|
|
connect(m_bodyEditor, &RichTextEditor::signatureClicked, this, &ComposeView::onSignatureClicked);
|
||
|
|
connect(m_bodyEditor, &RichTextEditor::signatureEditRequested, this, &ComposeView::onSignatureEditRequested);
|
||
|
|
}
|
||
|
|
'''
|
||
|
|
text = re.sub(pattern_setupUI, repl_setupUI, text)
|
||
|
|
|
||
|
|
# Replace onCcToggle slot (we renamed to onCcToggled with bool)
|
||
|
|
pattern_onCcToggle = re.compile(r'void ComposeView::onCcToggle\(\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE)
|
||
|
|
def repl_onCcToggle(match):
|
||
|
|
return '''void ComposeView::onCcToggled(bool checked)
|
||
|
|
{
|
||
|
|
m_ccRow->setVisible(checked);
|
||
|
|
if (checked) {
|
||
|
|
m_ccField->setFocus();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
'''
|
||
|
|
text = re.sub(pattern_onCcToggle, repl_onCcToggle, text)
|
||
|
|
|
||
|
|
# Replace onBccToggle similarly
|
||
|
|
pattern_onBccToggle = re.compile(r'void ComposeView::onBccToggle\(\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE)
|
||
|
|
def repl_onBccToggle(match):
|
||
|
|
return '''void ComposeView::onBccToggled(bool checked)
|
||
|
|
{
|
||
|
|
m_bccRow->setVisible(checked);
|
||
|
|
if (checked) {
|
||
|
|
m_bccField->setFocus();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
'''
|
||
|
|
text = re.sub(pattern_onBccToggle, repl_onBccToggle, text)
|
||
|
|
|
||
|
|
# Replace onSendClicked
|
||
|
|
pattern_onSendClicked = re.compile(r'void ComposeView::onSendClicked\(\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE)
|
||
|
|
def repl_onSendClicked(match):
|
||
|
|
return '''void ComposeView::onSendClicked()
|
||
|
|
{
|
||
|
|
QString to = m_toField->text();
|
||
|
|
QString cc = m_ccButton->isChecked() ? m_ccField->text() : QString();
|
||
|
|
QString bcc = m_bccButton->isChecked() ? m_bccField->text() : QString();
|
||
|
|
QString subject = m_subjectField->text();
|
||
|
|
QString body = m_bodyEditor->toHtml();
|
||
|
|
QDateTime scheduleTime = m_schedulePanel->isVisible() ? m_schedulePicker->dateTime() : QDateTime();
|
||
|
|
QString fromAddress;
|
||
|
|
if (m_currentAccountId > 0 && m_accountCombo->currentIndex() > 0) {
|
||
|
|
fromAddress = m_accountCombo->currentData().toString();
|
||
|
|
}
|
||
|
|
emit sendRequested(to, cc, bcc, subject, body, scheduleTime, fromAddress);
|
||
|
|
}
|
||
|
|
'''
|
||
|
|
text = re.sub(pattern_onSendClicked, repl_onSendClicked, text)
|
||
|
|
|
||
|
|
# Replace initializeComposition
|
||
|
|
pattern_initComp = re.compile(r'void ComposeView::initializeComposition\(\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE)
|
||
|
|
def repl_initComp(match):
|
||
|
|
return '''void ComposeView::initializeComposition()
|
||
|
|
{
|
||
|
|
m_subjectField->clear();
|
||
|
|
m_bodyEditor->clear();
|
||
|
|
m_toField->clear();
|
||
|
|
m_ccField->clear();
|
||
|
|
m_bccField->clear();
|
||
|
|
m_ccRow->setVisible(false);
|
||
|
|
m_bccRow->setVisible(false);
|
||
|
|
m_schedulePanel->setVisible(false);
|
||
|
|
m_subjectField->setFocus();
|
||
|
|
}
|
||
|
|
'''
|
||
|
|
text = re.sub(pattern_initComp, repl_initComp, text)
|
||
|
|
|
||
|
|
# Replace startNewEmail
|
||
|
|
pattern_startNew = re.compile(r'void ComposeView::startNewEmail\(const QString \&initialRecipient\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE)
|
||
|
|
def repl_startNew(match):
|
||
|
|
return '''void ComposeView::startNewEmail(const QString &initialRecipient)
|
||
|
|
{
|
||
|
|
initializeComposition();
|
||
|
|
if (!initialRecipient.isEmpty()) {
|
||
|
|
m_toField->setText(initialRecipient);
|
||
|
|
}
|
||
|
|
m_toField->setFocus();
|
||
|
|
}
|
||
|
|
'''
|
||
|
|
text = re.sub(pattern_startNew, repl_startNew, text)
|
||
|
|
|
||
|
|
# Replace setTo
|
||
|
|
pattern_setTo = re.compile(r'void ComposeView::setTo\(const QString \&to\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE)
|
||
|
|
def repl_setTo(match):
|
||
|
|
return '''void ComposeView::setTo(const QString &to)
|
||
|
|
{
|
||
|
|
if (to.isEmpty()) {
|
||
|
|
m_toField->clear();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// Split by semicolon or comma
|
||
|
|
QStringList parts = to.split(QRegularExpression(\"[,;]\"), Qt::SkipEmptyParts);
|
||
|
|
QStringList cleaned;
|
||
|
|
for (const QString &part : parts) {
|
||
|
|
QString trimmed = part.trimmed();
|
||
|
|
if (!trimmed.isEmpty())
|
||
|
|
cleaned << trimmed;
|
||
|
|
}
|
||
|
|
m_toField->setText(cleaned.join(\"; \"));
|
||
|
|
}
|
||
|
|
'''
|
||
|
|
text = re.sub(pattern_setTo, repl_setTo, text)
|
||
|
|
|
||
|
|
# We also need to add a helper function splitAddresses if we used it, but we didn't.
|
||
|
|
# Ensure we have the forward declaration for the slot in header (already done).
|
||
|
|
# Now write the file.
|
||
|
|
with open('composeview.cpp', 'w') as f:
|
||
|
|
f.write(text)
|