Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(forms): connecte le formulaire d'article au formulaire GitHub #170

Merged
merged 1 commit into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions qtribu/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

ICON_ARTICLE = QIcon(str(DIR_PLUGIN_ROOT.joinpath("resources/images/article.svg")))
ICON_GEORDP = QIcon(str(DIR_PLUGIN_ROOT.joinpath("resources/images/geordp.svg")))
LOCAL_CDN_PATH: Path = Path().home() / ".geotribu/cdn/"


# Classes
Expand Down
164 changes: 80 additions & 84 deletions qtribu/gui/form_article.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,27 @@
# standard
from functools import partial
from pathlib import Path
from typing import Optional, Union

# PyQGIS
from qgis.PyQt import uic
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QDialog
from qgis.PyQt.QtWidgets import QDialog, QDialogButtonBox, QWidget

# plugin
from qtribu.__about__ import DIR_PLUGIN_ROOT
from qtribu.constants import GEORDP_NEWS_ICONS, GeotribuImage
from qtribu.__about__ import __title__, __version__
from qtribu.constants import ICON_ARTICLE
from qtribu.toolbelt import NetworkRequestsManager, PlgLogger, PlgOptionsManager
from qtribu.toolbelt.commons import open_url_in_browser


class ArticleForm(QDialog):
"""QDialog form to submit an article."""

LOCAL_CDN_PATH: Path = Path().home() / ".geotribu/cdn/"
ISSUE_FORM_BASE_URL: str = (
"https://github.com/geotribu/website/issues/new?template=ARTICLE.yml"
)

def __init__(self, parent=None):
def __init__(self, parent: Optional[QWidget] = None):
"""Constructor.

:param parent: parent widget or application
Expand All @@ -42,84 +43,30 @@ def __init__(self, parent=None):
self.qntwk = NetworkRequestsManager()

# custom icon
self.setWindowIcon(QIcon(str(DIR_PLUGIN_ROOT / "resources/images/news.png")))

# icon combobox
self.cbb_icon_populate()
self.cbb_icon.textActivated.connect(self.cbb_icon_selected)
self.setWindowIcon(ICON_ARTICLE)

# publication
self.chb_license.setChecked(
self.plg_settings.get_value_from_key(
key="license_global_accept", exp_type=bool
)
self.cbb_license.addItems(
[
"Creative Commons International BY-NC-SA 4.0",
"Creative Commons International BY-SA 4.0",
"Beerware (Révision 42)",
"autre - merci de préciser dans le champ libre en fin de formulaire",
]
)

# connect help button
self.btn_box.helpRequested.connect(
partial(
open_url_in_browser,
"https://contribuer.geotribu.fr/rdp/add_news/",
"https://contribuer.geotribu.fr/articles/workflow/",
)
)
self.btn_box.button(QDialogButtonBox.Ok).clicked.connect(self.on_btn_submit)
self.btn_box.button(QDialogButtonBox.Ok).setDefault(True)
self.btn_box.button(QDialogButtonBox.Ok).setText(self.tr("Submit"))

def cbb_icon_populate(self) -> None:
"""Populate combobox of article icons."""
# save current index
current_item_idx = self.cbb_icon.currentIndex()

# clear
self.cbb_icon.clear()

# populate
self.cbb_icon.addItem("", None)
for rdp_icon in GEORDP_NEWS_ICONS:
if rdp_icon.kind != "icon":
continue

if rdp_icon.local_path().is_file():
self.cbb_icon.addItem(
QIcon(str(rdp_icon.local_path().resolve())), rdp_icon.name, rdp_icon
)
else:
self.cbb_icon.addItem(rdp_icon.name, rdp_icon)

# icon tooltip
self.cbb_icon.setItemData(
GEORDP_NEWS_ICONS.index(rdp_icon) + 1,
rdp_icon.description,
Qt.ToolTipRole,
)

# restore current index
self.cbb_icon.setCurrentIndex(current_item_idx)

def cbb_icon_selected(self) -> None:
"""Download selected icon locally if it doesn't exist already."""
selected_icon: GeotribuImage = self.cbb_icon.currentData()
if not selected_icon:
return

icon_local_path = selected_icon.local_path()
if not icon_local_path.is_file():
self.log(
message=f"Icon doesn't exist locally: {icon_local_path}", log_level=4
)
icon_local_path.parent.mkdir(parents=True, exist_ok=True)
self.qntwk.download_file(
remote_url=selected_icon.url,
local_path=str(icon_local_path.resolve()),
)
# repopulate combobx to get updated items icons
self.cbb_icon_populate()

def accept(self) -> bool:
"""Auto-connected to the OK button (within the button box), i.e. the `accepted`
signal. Check if required form fields are correctly filled.

:return: False if some check fails. True and emit accepted() signal if everything is ok.
:rtype: bool
"""
def check_required_fields(self) -> bool:
invalid_fields = []
error_message = ""

Expand All @@ -131,21 +78,21 @@ def accept(self) -> bool:
)

# check description
if len(self.txt_description.toPlainText()) < 25:
if len(self.txt_description.toPlainText()) < 10:
invalid_fields.append(self.txt_description)
error_message += self.tr(
"- Description is not long enough (25 characters at least).\n"
"- The description is not long enough (10 characters at least).\n"
)
if len(self.txt_description.toPlainText()) > 160:

# check date
if not len(self.dte_proposed_date.date().toString("dd/MM/yyyy")):
invalid_fields.append(self.txt_description)
error_message += self.tr(
"- Description is too long (160 characters maximum).\n"
)
error_message += self.tr("- Date has to be filled.\n")

# check license
if not self.chb_license.isChecked():
invalid_fields.append(self.chb_license)
error_message += self.tr("- License must be accepted.\n")
# if not self.cbb_license.isChecked():
# invalid_fields.append(self.chb_license)
# error_message += self.tr("- License must be accepted.\n")

# check author firstname
if len(self.wdg_author.lne_firstname.text()) < 2:
Expand Down Expand Up @@ -186,6 +133,55 @@ def accept(self) -> bool:
for wdg in invalid_fields:
wdg.setStyleSheet("border: 1px solid red;")
return False
else:

return True

def on_btn_submit(self) -> Union[bool, str, None]:
"""Check if required form fields are correctly filled and submit to Github issue
form.

:return: False if some check fails. True and emit accepted() signal if
everything is ok.
:rtype: bool
"""
if not self.check_required_fields():
return False

completed_url = (
f"{self.ISSUE_FORM_BASE_URL}"
f"&in_author_name={self.wdg_author.lne_firstname.text()} "
f"{self.wdg_author.lne_lastname.text()}"
f"&in_author_mail={self.wdg_author.lne_email.text()}"
f"&in_author_linkedin={self.wdg_author.lne_linkedin_account.text()}"
f"&in_author_mastodon={self.wdg_author.lne_mastodon_account.text()}"
f"&in_author_twitter={self.wdg_author.lne_twitter_account.text()}"
f"&in_author_license=true"
Guts marked this conversation as resolved.
Show resolved Hide resolved
f"&cb_author_content_relationship={self.chb_transparency.isChecked()}"
f"&in_art_title={self.lne_title.text()}"
f"&in_art_date={self.dte_proposed_date.date().toString('dd/MM/yyyy')}"
f"&tx_art_content={self.txt_description.toPlainText()}"
Guts marked this conversation as resolved.
Show resolved Hide resolved
f"&tx_misc_comment={self.txt_comment.toPlainText()} "
f"\n---\n\n{__title__} {__version__}"
f"&title=[Proposition] {self.lne_title.text()} - {__title__} {__version__}"
)
self.log(message=f"Opening issue form: {completed_url}", log_level=4)
url_opened = open_url_in_browser(url=completed_url)
if url_opened:
self.log(
message=self.tr("Issue form URL opened in default system web browser."),
log_level=4,
)
super().accept()
return True
else:
self.log(
parent_location=self,
message=self.tr(
"Opening issue form URL in default system web browser failed. "
"Check if there is any special characters in form fields and try again."
),
push=True,
duration=10,
log_level=2,
)
return False
Loading
Loading