Improve system configuration

Julien LepillerTue Dec 07 03:26:02+0100 2021

0f51638

Improve system configuration

offlate/core/config.py

5959
        self.hasRequiredRow = hasRequiredRow
6060
6161
    def isConfigured(self, conf, data):
62+
        if self.optional:
63+
            return True
64+
6265
        if not ConfigSpec.isConfigured(self, conf):
6366
            return False
6467

6972
                return False
7073
        
7174
        return True
75+
76+
class RowConfigSpec(ConfigSpec):
77+
    """
78+
    The specification of a row in a list of configurations.
79+
    """
80+
    def __init__(self, key, name, description, specifications, indexKey,
81+
            indexValue, link=None, optional=False):
82+
        ConfigSpec.__init__(self, key, name, description, optional, link)
83+
        self.specifications = specifications
84+
        self.indexKey = indexKey
85+
        self.indexValue = indexValue
86+
87+
    def isConfigured(self, conf, data):
88+
        if self.optional:
89+
            return True
90+
91+
        if not ConfigSpec.isConfigured(self, conf):
92+
            return False
93+
94+
        for conf in self.specifications:
95+
            if not conf.isConfigured(conf, data) and not conf.optional:
96+
                return False
97+
98+
        return True

offlate/systems/git.py

165165
        for x in Project.translationfiles:
166166
            x['format'].reload()
167167
168-
    def getSystemConfigSpec():
168+
    def getSystemConfigSpec(data=None):
169169
        return []

offlate/systems/github.py

9494
                    placeholder=Project.tr('master'))]
9595
9696
    @staticmethod
97-
    def getSystemConfigSpec():
97+
    def getSystemConfigSpec(data=None):
9898
        specs = [StringConfigSpec('token', Project.tr('Token'),
9999
            Project.tr('You can get a token from <a href=\"#\">https://github.com/settings/tokens/new</a>. \
100100
You will need at least to grant the public_repo permission.'),

offlate/systems/gitlab.py

9797
                    Project.tr('master'))]
9898
9999
    @staticmethod
100-
    def getSystemConfigSpec():
100+
    def getSystemConfigSpec(data=None):
101101
        specs = []
102102
        specs.extend(GitProject.getSystemConfigSpec())
103103
        specs.append(

offlate/systems/project.py

123123
        return True
124124
125125
    @staticmethod
126-
    def getSystemConfigSpec():
126+
    def getSystemConfigSpec(data=None):
127127
        """
128128
        Each system has two kinds of configuration: a global configuration used
129129
        by any instance of the same system (eg: an API key for a given system),

offlate/systems/tp.py

155155
            optional=True)]
156156
157157
    @staticmethod
158-
    def getSystemConfigSpec():
158+
    def getSystemConfigSpec(data=None):
159159
        return [StringConfigSpec('server', Project.tr('Mail server'),
160160
                Project.tr('To send your work to the translation project on \
161161
your behalf, we need to know the email server you are going to use (usually \

offlate/systems/transifex.py

143143
            x.reload()
144144
145145
    @staticmethod
146-
    def getSystemConfigSpec():
146+
    def getSystemConfigSpec(data=None):
147147
        return [StringConfigSpec('token', Project.tr('Token'),
148148
            Project.tr('You can get a token from <a href=\"#\">https://www.transifex.com/user/settings/api/</a>'),
149149
            link='https://www.transifex.com/user/settings/api/')]

offlate/systems/weblate.py

220220
                    Project.tr('foo'))]
221221
222222
    @staticmethod
223-
    def getSystemConfigSpec():
224-
        return [ListConfigSpec('servers', Project.tr('Configured Weblate instances'),
223+
    def getSystemConfigSpec(data=None):
224+
        if data is not None:
225+
            print('weblate: {}'.format(data))
226+
            return [RowConfigSpec('servers', Project.tr('Weblate instance configuration'),
227+
                    Project.tr('You need to configure each Weblate instance separately, \
228+
and you haven\'t configured the instance at {} yet.').format(data['instance']),
229+
                    [
230+
                        StringConfigSpec('token', Project.tr('Token'),
231+
                            Project.tr('The token you created from your account. \
232+
You can create it from <a href="#">The API access tab</a> in your account settings'),
233+
                            link = "{}/accounts/profile/#api".format(data['instance']),
234+
                            placeholder = Project.tr('fKbStkBgFzIL0UW15sfcJh7kC0BAbcVtV16kblXlM'))
235+
                    ], 'server', data['instance'])]
236+
        else:
237+
            return [ListConfigSpec('servers', Project.tr('Configured Weblate instances'),
225238
                    Project.tr('You need to find a token for each Weblate instance \
226239
you have an account on. You can create a token by logging into your account, \
227240
going to your settings and in the API Access page.'),

offlate/ui/config/settings.py

2828
from ...systems.list import systems
2929
from ...core.manager import ProjectManager
3030
from ...core.config import *
31-
from ..listsettingsedit import ListSettingsEdit
32-
33-
class SettingsLineEdit(QLineEdit):
34-
    def content(self):
35-
        return self.text()
36-
37-
    def setContent(self, value):
38-
        self.setText(value)
31+
from ..settingsedit import ListSettingsEdit, RowSettingsEdit, SettingsLineEdit
3932
4033
class SettingsWindow(QMainWindow):
4134
    def __init__(self, manager):

5548
        super().show()
5649
5750
class SystemSettingsWidget(QWidget):
58-
    def __init__(self, manager, system = -1, parent=None):
51+
    def __init__(self, manager, system = -1, data=None, parent=None):
5952
        super().__init__(parent)
6053
        self.system = systems[system]
6154
        self.manager = manager
55+
        self.data = data
6256
        self.initUI()
6357
6458
    def initUI(self):

6660
        name = self.system['name']
6761
        key = self.system['key']
6862
        system = self.system['system']
69-
        spec = system.getSystemConfigSpec()
63+
        spec = system.getSystemConfigSpec(self.data)
7064
71-
        self.data = self.manager.settings.conf
72-
        if not key in self.data:
73-
            self.data[key] = {}
65+
        self.conf = self.manager.settings.conf
66+
        if not key in self.conf:
67+
            self.conf[key] = {}
7468
        self.widgets = {key: {}}
7569
7670
        formBox = QGroupBox(self.tr(name))
7771
        formLayout = QFormLayout()
7872
7973
        for s in spec:
80-
            label = QLabel(self.tr(s.description))
81-
            label.setWordWrap(True)
82-
            label.setOpenExternalLinks(True)
8374
            widget = None
8475
            if isinstance(s, StringConfigSpec):
8576
                widget = SettingsLineEdit()
8677
            elif isinstance(s, ListConfigSpec):
8778
                widget = ListSettingsEdit(s)
79+
            elif isinstance(s, RowConfigSpec):
80+
                widget = RowSettingsEdit(s)
8881
            else:
8982
                raise Exception('Unknown spec type ' + str(s))
9083
9184
            try:
92-
                widget.setContent(self.data[key][s.key])
85+
                widget.setContent(self.conf[key][s.key])
9386
            except Exception:
9487
                pass
9588
            widget.textChanged.connect(self.update)
89+
9690
            formLayout.addRow(QLabel(self.tr(s.name)), widget)
91+
            label = QLabel(self.tr(s.description))
92+
            label.setWordWrap(True)
93+
            label.setOpenExternalLinks(True)
9794
            formLayout.addRow(label)
9895
            self.widgets[key][s.key] = widget
9996

104101
    def update(self):
105102
        key = self.system['key']
106103
        system = self.system['system']
107-
        spec = system.getSystemConfigSpec()
104+
        spec = system.getSystemConfigSpec(self.data)
108105
        for s in spec:
109106
            widget = self.widgets[key][s.key]
110-
            self.data[key][s.key] = widget.content()
107+
            self.conf[key][s.key] = widget.content()
111108
112109
class SystemSettingsWindow(QDialog):
113-
    def __init__(self, manager, system = -1, parent = None):
110+
    def __init__(self, manager, system = -1, data=None, parent = None):
114111
        super().__init__(parent)
115-
        self.widget = SystemSettingsWidget(manager, system, self)
112+
        self.widget = SystemSettingsWidget(manager, system, data, self)
116113
117114
        buttons = QHBoxLayout()
118115
        buttons.addStretch(1)

332329
        self.parent.hide()
333330
334331
    def openSettings(self, system):
335-
        w = SystemSettingsWindow(self.manager, system, self)
332+
        w = SystemSettingsWindow(self.manager, system, parent = self)
336333
        w.exec_()
337334
338335
    def getSettingsOpener(self, system):

offlate/ui/listsettingsedit.py unknown status 2

1-
#   Copyright (c) 2018 Julien Lepiller <julien@lepiller.eu>
2-
#
3-
#   This program is free software: you can redistribute it and/or modify
4-
#   it under the terms of the GNU Affero General Public License as
5-
#   published by the Free Software Foundation, either version 3 of the
6-
#   License, or (at your option) any later version.
7-
#
8-
#   This program is distributed in the hope that it will be useful,
9-
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
10-
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11-
#   GNU Affero General Public License for more details.
12-
#
13-
#   You should have received a copy of the GNU Affero General Public License
14-
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
15-
####
16-
17-
from PyQt5.QtWidgets import *
18-
from PyQt5.QtGui import *
19-
from PyQt5.QtCore import *
20-
21-
class ListSettingsRowEdit(QDialog):
22-
    def __init__(self, specifications, content, parent = None):
23-
        super().__init__(parent)
24-
        self.content = content
25-
        self.specifications = specifications
26-
        self.widgets = {}
27-
        self.initUI()
28-
29-
    def initUI(self):
30-
        vbox = QVBoxLayout()
31-
        hbox = QHBoxLayout()
32-
33-
        for s in self.specifications:
34-
            edit = QLineEdit()
35-
            edit.setPlaceholderText(self.tr(s.placeholder))
36-
            edit.setText(self.content[s.key])
37-
            self.widgets[s.key] = edit
38-
            hbox.addWidget(edit)
39-
40-
        vbox.addLayout(hbox)
41-
42-
        buttons = QHBoxLayout()
43-
        buttons.addStretch(1)
44-
        okbutton = QPushButton(self.tr("OK"))
45-
        buttons.addWidget(okbutton)
46-
        okbutton.clicked.connect(self.save)
47-
        vbox.addLayout(buttons)
48-
        self.setLayout(vbox)
49-
50-
    def save(self):
51-
        self.content = {}
52-
        for s in self.specifications:
53-
            self.content[s.key] = self.widgets[s.key].text()
54-
        self.close()
55-
56-
class ListSettingsEdit(QWidget):
57-
    textChanged = pyqtSignal()
58-
59-
    def __init__(self, conf, parent = None):
60-
        super(ListSettingsEdit, self).__init__(parent)
61-
        self.conf = conf
62-
        self.initUI()
63-
64-
    def addLine(self, data):
65-
        items = [QTreeWidgetItem(data)]
66-
        self.treeWidget.addTopLevelItems(items)
67-
68-
    def addLineSlot(self):
69-
        d = []
70-
        for i in range(0, len(self.conf.specifications)):
71-
            d.append(self.widgets[i].text())
72-
        items = [QTreeWidgetItem(d)]
73-
        self.treeWidget.addTopLevelItems(items)
74-
        self.textChanged.emit()
75-
76-
    def deleteLineSlot(self):
77-
        self.treeWidget.takeTopLevelItem(self.treeWidget.currentIndex().row())
78-
        self.textChanged.emit()
79-
80-
    def content(self):
81-
        number = self.treeWidget.topLevelItemCount()
82-
        specs = self.conf.specifications
83-
        items = []
84-
        for i in range(0, number):
85-
            item = self.treeWidget.topLevelItem(i)
86-
            data = {}
87-
            j = 0
88-
            for s in specs:
89-
                data[s.key] = item.text(j)
90-
                j += 1
91-
            items.append(data)
92-
        return items
93-
94-
    def setContent(self, data):
95-
        for d in data:
96-
            line = []
97-
            for s in self.conf.specifications:
98-
                line.append(d[s.key])
99-
            self.addLine(line)
100-
101-
    def editLine(self, item, column):
102-
        specs = self.conf.specifications
103-
        data = {}
104-
        i = 0
105-
        for s in specs:
106-
            data[s.key] = item.text(i)
107-
            i += 1
108-
109-
        w = ListSettingsRowEdit(specs, data, self)
110-
        w.exec_()
111-
        i = 0
112-
        for s in specs:
113-
            item.setText(i, w.content[s.key])
114-
            i += 1
115-
        self.textChanged.emit()
116-
117-
    def initUI(self):
118-
        vbox = QVBoxLayout()
119-
        hbox = QHBoxLayout()
120-
        self.setLayout(vbox)
121-
        self.treeWidget = QTreeWidget()
122-
        self.treeWidget.setColumnCount(len(self.conf.specifications))
123-
        self.treeWidget.itemDoubleClicked.connect(self.editLine)
124-
        vbox.addWidget(self.treeWidget)
125-
126-
        self.widgets = []
127-
        for s in self.conf.specifications:
128-
            edit = QLineEdit()
129-
            edit.setPlaceholderText(self.tr(s.placeholder))
130-
            self.widgets.append(edit)
131-
            hbox.addWidget(edit)
132-
133-
        addbutton = QPushButton(self.tr("Add"))
134-
        addbutton.clicked.connect(self.addLineSlot)
135-
        removebutton = QPushButton(self.tr("Remove"))
136-
        removebutton.clicked.connect(self.deleteLineSlot)
137-
138-
        hbox.addWidget(addbutton)
139-
        hbox.addWidget(removebutton)
140-
        vbox.addLayout(hbox)

offlate/ui/manager.py

165165
            return
166166
        res = self.manager.isConfigured(w.getProjectSystem(), w.getProjectInfo())
167167
        while not res:
168-
            if not self.configureSystem(w.getProjectSystem()):
168+
            if not self.configureSystem(w.getProjectSystem(), w.getProjectInfo()):
169169
                return
170170
            res = self.manager.isConfigured(w.getProjectSystem(), w.getProjectInfo())
171171
        worker = NewRunnable(self, w.getProjectName(), w.getProjectLang(),

184184
            return
185185
        res = self.manager.isConfigured(w.getProjectSystem(), w.getProjectInfo())
186186
        while not res:
187-
            if not self.configureSystem(w.getProjectSystem()):
187+
            if not self.configureSystem(w.getProjectSystem(), w.getProjectInfo()):
188188
                return
189189
            res = self.manager.isConfigured(w.getProjectSystem(), w.getProjectInfo())
190190
        worker = NewRunnable(self, w.getProjectName(), w.getProjectLang(),

195195
        worker.signals.restart_required.connect(self.restartNew)
196196
        self.threadpool.start(worker)
197197
198-
    def configureSystem(self, system):
199-
        w = SystemSettingsWindow(self.manager, system)
198+
    def configureSystem(self, system, data=None):
199+
        w = SystemSettingsWindow(self.manager, system, data)
200200
        w.exec_()
201201
        if w.done:
202202
            self.manager.updateSettings(w.data())

offlate/ui/settingsedit.py unknown status 1

1+
#   Copyright (c) 2018 Julien Lepiller <julien@lepiller.eu>
2+
#
3+
#   This program is free software: you can redistribute it and/or modify
4+
#   it under the terms of the GNU Affero General Public License as
5+
#   published by the Free Software Foundation, either version 3 of the
6+
#   License, or (at your option) any later version.
7+
#
8+
#   This program is distributed in the hope that it will be useful,
9+
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11+
#   GNU Affero General Public License for more details.
12+
#
13+
#   You should have received a copy of the GNU Affero General Public License
14+
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
15+
####
16+
17+
from PyQt5.QtWidgets import *
18+
from PyQt5.QtGui import *
19+
from PyQt5.QtCore import *
20+
21+
from ..core.config import *
22+
23+
class SettingsLineEdit(QLineEdit):
24+
    def content(self):
25+
        return self.text()
26+
27+
    def setContent(self, value):
28+
        self.setText(value)
29+
30+
class ListSettingsRowEdit(QDialog):
31+
    def __init__(self, specifications, content, parent = None):
32+
        super().__init__(parent)
33+
        self.content = content
34+
        self.specifications = specifications
35+
        self.widgets = {}
36+
        self.initUI()
37+
38+
    def initUI(self):
39+
        vbox = QVBoxLayout()
40+
        hbox = QHBoxLayout()
41+
42+
        for s in self.specifications:
43+
            edit = QLineEdit()
44+
            edit.setPlaceholderText(self.tr(s.placeholder))
45+
            edit.setText(self.content[s.key])
46+
            self.widgets[s.key] = edit
47+
            hbox.addWidget(edit)
48+
49+
        vbox.addLayout(hbox)
50+
51+
        buttons = QHBoxLayout()
52+
        buttons.addStretch(1)
53+
        okbutton = QPushButton(self.tr("OK"))
54+
        buttons.addWidget(okbutton)
55+
        okbutton.clicked.connect(self.save)
56+
        vbox.addLayout(buttons)
57+
        self.setLayout(vbox)
58+
59+
    def save(self):
60+
        self.content = {}
61+
        for s in self.specifications:
62+
            self.content[s.key] = self.widgets[s.key].text()
63+
        self.close()
64+
65+
class ListSettingsEdit(QWidget):
66+
    textChanged = pyqtSignal()
67+
68+
    def __init__(self, conf, parent = None):
69+
        super(ListSettingsEdit, self).__init__(parent)
70+
        self.conf = conf
71+
        self.initUI()
72+
73+
    def addLine(self, data):
74+
        items = [QTreeWidgetItem(data)]
75+
        self.treeWidget.addTopLevelItems(items)
76+
77+
    def addLineSlot(self):
78+
        d = []
79+
        for i in range(0, len(self.conf.specifications)):
80+
            d.append(self.widgets[i].text())
81+
        items = [QTreeWidgetItem(d)]
82+
        self.treeWidget.addTopLevelItems(items)
83+
        self.textChanged.emit()
84+
85+
    def deleteLineSlot(self):
86+
        self.treeWidget.takeTopLevelItem(self.treeWidget.currentIndex().row())
87+
        self.textChanged.emit()
88+
89+
    def content(self):
90+
        number = self.treeWidget.topLevelItemCount()
91+
        specs = self.conf.specifications
92+
        items = []
93+
        for i in range(0, number):
94+
            item = self.treeWidget.topLevelItem(i)
95+
            data = {}
96+
            j = 0
97+
            for s in specs:
98+
                data[s.key] = item.text(j)
99+
                j += 1
100+
            items.append(data)
101+
        return items
102+
103+
    def setContent(self, data):
104+
        for d in data:
105+
            line = []
106+
            for s in self.conf.specifications:
107+
                line.append(d[s.key])
108+
            self.addLine(line)
109+
110+
    def editLine(self, item, column):
111+
        specs = self.conf.specifications
112+
        data = {}
113+
        i = 0
114+
        for s in specs:
115+
            data[s.key] = item.text(i)
116+
            i += 1
117+
118+
        w = ListSettingsRowEdit(specs, data, self)
119+
        w.exec_()
120+
        i = 0
121+
        for s in specs:
122+
            item.setText(i, w.content[s.key])
123+
            i += 1
124+
        self.textChanged.emit()
125+
126+
    def initUI(self):
127+
        vbox = QVBoxLayout()
128+
        hbox = QHBoxLayout()
129+
        self.setLayout(vbox)
130+
        self.treeWidget = QTreeWidget()
131+
        self.treeWidget.setColumnCount(len(self.conf.specifications))
132+
        self.treeWidget.itemDoubleClicked.connect(self.editLine)
133+
        vbox.addWidget(self.treeWidget)
134+
135+
        self.widgets = []
136+
        for s in self.conf.specifications:
137+
            edit = QLineEdit()
138+
            edit.setPlaceholderText(self.tr(s.placeholder))
139+
            self.widgets.append(edit)
140+
            hbox.addWidget(edit)
141+
142+
        addbutton = QPushButton(self.tr("Add"))
143+
        addbutton.clicked.connect(self.addLineSlot)
144+
        removebutton = QPushButton(self.tr("Remove"))
145+
        removebutton.clicked.connect(self.deleteLineSlot)
146+
147+
        hbox.addWidget(addbutton)
148+
        hbox.addWidget(removebutton)
149+
        vbox.addLayout(hbox)
150+
151+
class RowSettingsEdit(QWidget):
152+
    textChanged = pyqtSignal()
153+
154+
    def __init__(self, conf, parent = None):
155+
        super(RowSettingsEdit, self).__init__(parent)
156+
        self.conf = conf
157+
        self.data = []
158+
        self.initUI()
159+
160+
    def initUI(self):
161+
        formLayout = QFormLayout()
162+
        self.widgets = {}
163+
164+
        self.row = {}
165+
        self.row[self.conf.indexKey] = self.conf.indexValue
166+
        for s in self.conf.specifications:
167+
            widget = None
168+
            if isinstance(s, StringConfigSpec):
169+
                widget = SettingsLineEdit()
170+
            elif isinstance(s, ListConfigSpec):
171+
                widget = ListSettingsEdit(s)
172+
            elif isinstance(s, RowConfigSpec):
173+
                widget = RowSettingsEdit(s)
174+
            else:
175+
                raise Exception('Unknown spec type ' + str(s))
176+
177+
            try:
178+
                widget.setContent(self.row[s.key])
179+
            except Exception:
180+
                pass
181+
            widget.textChanged.connect(self.update)
182+
183+
            formLayout.addRow(QLabel(self.tr(s.name)), widget)
184+
            label = QLabel(self.tr(s.description))
185+
            label.setWordWrap(True)
186+
            label.setOpenExternalLinks(True)
187+
            formLayout.addRow(label)
188+
            self.widgets[s.key] = widget
189+
190+
        self.setLayout(formLayout)
191+
192+
    def update(self):
193+
        for s in self.conf.specifications:
194+
            widget = self.widgets[s.key]
195+
            value = widget.content()
196+
            self.row[s.key] = value
197+
        self.textChanged.emit()
198+
199+
    def setContent(self, data):
200+
        self.data = data
201+
        for d in data:
202+
            if d[self.conf.indexKey] == self.conf.indexValue:
203+
                self.row = d
204+
205+
    def content(self):
206+
        hasRow = False
207+
        for d in self.data:
208+
            print(d)
209+
            if d[self.conf.indexKey] == self.conf.indexValue:
210+
                hasRow = True
211+
212+
        if hasRow:
213+
            self.data = [self.row if x[self.conf.indexKey] == self.conf.indexValue else x for x in self.data]
214+
        else:
215+
            self.data.append(self.row)
216+
217+
        return self.data

offlate/ui/welcome.py

170170
        email = self.field("email")
171171
        name = self.field("name")
172172
        self.parent.manager.updateSettings({'Generic': {'name': name, 'email': email, 'lang': lang}})
173-
        widget = SystemSettingsWidget(self.parent.manager, system, self)
173+
        widget = SystemSettingsWidget(self.parent.manager, system = system, parent = self)
174174
        layout.addWidget(widget)
175175
        self.setLayout(layout)
176176