def processing_data(self): """Processing data from all step""" self.progress_bar.setVisible(True) self.submit_button.setVisible(False) self.parent.back_button.setEnabled(False) self.set_status(tr('Processing data')) self.set_progress_bar(self.current_progress + 25) self.set_progress_bar(self.current_progress + 25) self.set_progress_bar(self.current_progress + 25) self.data = self.step_1_data self.data['questionnaire'] = self.questionnaire # Finalize the data for location in self.data['locations']['features']: for cadasta_field, layer_field in self.step_2_data.iteritems(): properties = location['properties'] if layer_field in properties: try: location['fields'] except KeyError: location['fields'] = dict() location['fields'][cadasta_field] = properties[layer_field] del location['properties'][layer_field] self.set_progress_bar(100) self.set_status(tr('Finished processing data')) # Upload project self.upload_project()
def set_widgets(self): """Set all widgets.""" self.text_test_connection_button = self.test_connection_button.text() self.ok_label.setVisible(False) self.save_button.setEnabled(False) self.test_connection_button.clicked.connect(self.login) self.save_button.clicked.connect(self.save_authtoken) self.clear_button.setText(tr('Clear')) self.clear_button.setEnabled(True) self.clear_button.clicked.connect(self.clear_information) self.url_input.setText(get_url_instance()) # If login information exists if not self.auth_token: self.auth_token = get_authtoken() if self.auth_token: self.test_connection_button.setEnabled(False) self.username_input.setText(get_setting('username')) self.username_input.setReadOnly(True) self.password_input.setReadOnly(True) self.username_input.setStyleSheet( "QLineEdit { background-color: '#d9d9d9'; color: '#5b5b5b'; " "selection-background-color: '#969292'; }") self.password_input.setStyleSheet( "QLineEdit { background-color: '#d9d9d9'; color: '#5b5b5b'; " "selection-background-color: '#969292'; }") self.token_status.setText(tr('Auth token is saved.')) else: self.token_status.setText(tr('Auth token is empty.'))
def extract_error_detail(result): """Extract detail of error of connection :param result: result of connection :type result: str :return: detail result :rtype:str """ error_detail = tr('Error : ') detail = '' try: json_result = json.loads(result) # get code code = json_result['code'] error_detail = tr('Error %s : ' % str(code)) try: # get detail detail = json_result['result']['detail'] except (TypeError, KeyError): detail = json.dumps(json_result['result']) except ValueError: detail = result error_detail += detail return '<span style="color:red">%s</span><br>' % error_detail
def upload_parties(self): """Upload party from this project.""" self.set_status(tr('Uploading parties')) party = 0 # reset progress bar current_progress = 0 self.set_progress_bar(current_progress) total_layer = len(self.data['locations']['features']) progress_block = 100 / total_layer post_url = self._url_post_parties() if not post_url: # Project is not uploaded return for layer in self.data['locations']['features']: if 'party_name' in layer['fields'] and \ 'party_type' in layer['fields']: post_data = { 'name': layer['fields']['party_name'], 'type': layer['fields']['party_type'] } post_data = Utilities.json_dumps(post_data) connector = ApiConnect(get_url_instance() + post_url) status, result = self._call_json_post(connector, post_data) if status: party += 1 try: result_dict = result if 'id' in result_dict: layer['party_id'] = result_dict['id'] except ValueError as e: LOGGER.exception('message') else: self.set_progress_bar(0) self.set_status(Utilities.extract_error_detail(result)) else: self.set_status(tr('No party attributes found')) current_progress += progress_block self.set_progress_bar(current_progress) if party == 0: self.set_status(tr('Not uploading any party')) else: self.set_status( tr('Finished uploading {party} party'.format(party=party))) self.set_progress_bar(100)
def upload_project(self): """Upload project to cadasta.""" # check requirement attributes if self.check_requirement(): return self.set_status(tr('Uploading project')) self.current_progress = 0 self.set_progress_bar(self.current_progress) post_data = { 'name': self.data['project_name'], 'description': self.data['description'], 'extent': self.data['extent'], 'access': 'private' if self.data['private'] else 'public', 'contacts': self.data['contacts'] } if self.data['project_url']: post_data['urls'] = [self.data['project_url']] post_url = '/api/v1/organizations/%s/projects/' % ( self.data['organisation']['slug']) post_data = Utilities.json_dumps(post_data) connector = ApiConnect(get_url_instance() + post_url) status, result = self._call_json_post(connector, post_data) self.set_progress_bar(self.current_progress + self.upload_increment) self.set_status(tr('Finished uploading project')) if status: self.project_upload_result = result upload_questionnaire_attribute = False # create questionnaire first # after creating location, questionnaire is blocked if self.data['questionnaire']: upload_questionnaire_attribute = True self.upload_questionnaire_project() total_locations = len(self.data['locations']['features']) if total_locations > 0: self.upload_locations(upload_questionnaire_attribute) self.upload_parties() self.upload_relationships() self.rerender_saved_layer() self.set_progress_bar(100) else: self.set_progress_bar(0) self.set_status(Utilities.extract_error_detail(result)) self.set_status(tr('Finished')) self.parent.close()
def upload_relationships(self): """Upload relationships attribute to cadasta.""" self.set_status(tr('Uploading relationship')) # reset progress bar current_progress = 0 self.set_progress_bar(current_progress) total_layer = len(self.data['locations']['features']) progress_block = 100 / total_layer relationship = 0 url = self._url_post_relationships() for layer in self.data['locations']['features']: if 'relationship_type' in layer['fields'] and \ 'spatial_id' in layer and \ 'party_id' in layer: post_data = { 'tenure_type': layer['fields']['relationship_type'], 'spatial_unit': layer['spatial_id'], 'party': layer['party_id'] } post_data = Utilities.json_dumps(post_data) connector = ApiConnect(get_url_instance() + url) status, result = self._call_json_post(connector, post_data) if status: relationship += 1 else: self.set_progress_bar(0) self.set_status(Utilities.extract_error_detail(result)) else: self.set_status(tr('No relationship attributes found')) current_progress += progress_block self.set_progress_bar(current_progress) if relationship == 0: self.set_status(tr('Not uploading any relationship')) else: self.set_status( tr('Finished uploading {num} relationship'.format( num=relationship))) self.set_progress_bar(100)
def content(): """Helper method that returns just the content. This method was added so that the text could be reused in the other contexts. :returns: A message object without brand element. :rtype: safe.messaging.message.Message """ message = m.Message() message.add( m.Paragraph(tr('The plugin integrates with the cadasta.org platform'))) message.add(m.Paragraph(tr('developed by kartoza.com'))) return message
def add(self, item): """add a Text MessageElement to the existing Text Strings can be passed and are automatically converted in to item.Text() Args: Text text, an element to add to the text Returns: None Raises: Errors are propagated """ if self._is_stringable(item) or self._is_qstring(item): self.items.append(PlainText(item)) elif isinstance(item, MessageElement): self.items.append(item) elif isinstance(item, QPyNullVariant): self.items.append( PlainText( tr('Null (PyQt4.QtCore.QPyNullVariant) found from the data.' ))) else: raise InvalidMessageItemError(item, item.__class__)
def on_next_button_released(self): """Handle the Next button release. .. note:: This is an automatic Qt slot executed when the Next button is released. """ current_step = self.get_current_step() valid_status, message = current_step.validate_step() if not valid_status: self.message_bar = QgsMessageBar() self.message_bar.pushWarning( tr('Error'), message ) LOGGER.info(message) return self.steps.append(current_step) # Determine the new step to be switched new_step = current_step.get_next_step() if new_step is not None: # Prepare the next tab self.prepare_the_next_step(new_step) new_step.set_widgets() else: # Wizard complete self.accept() return self.go_to_step(new_step)
def validate_step(self): """Check if the step is valid. :returns: Tuple of validation status and error message if any :rtype: ( bool, str ) """ error_message = '' if self.project_url() and \ not is_valid_url(self.project_url()): error_message = tr('Invalid url.') if not self.project_name() or \ not self.selected_organisation(): error_message = tr('Empty required field.') return (error_message == '', error_message)
def validate_step(self): """Check if the step is valid. :returns: Tuple of validation status and error message if any :rtype: ( bool, str ) """ error_message = '' if not self.location_type_box.currentText() or \ tr('No field') in self.location_type_box.currentText(): error_message = tr( 'Location type field is required, please select.' ) return ( error_message == '', error_message )
def set_widgets(self): """Set all widgets on the tab.""" self.text_edit.setStyleSheet( "background-color: #f0f0f0; color: #757575") self.progress_bar.setVisible(False) self.lbl_status.setText(tr('Are you sure to upload the data?')) self.submit_button.setFocus() self.step_1_data = self.parent.step_1_data() self.step_2_data, self.questionnaire = self.parent.step_2_data() # Validate questionnaire data self.questionnaire = self.validate_questionnaire(self.questionnaire)
def heading(): """Helper method that returns just the header. This method was added so that the text could be reused in the other contexts. :returns: A heading object. :rtype: safe.messaging.heading.Heading """ message = m.Heading(tr('Cadasta Help'), **INFO_STYLE) return message
def project_combo_box_changed(self): """Update description when combo box changed.""" project = self.selected_project() if not project: self.project_description_label.setText( tr('This is the read only project description that ' 'changes when the combo box above is changed.')) self.project_url_label.setText('-') self.contact_information_label.setText('-') self.privacy_status_label.setText('-') self.set_enabled_add_contact_label(False) return if project['description']: self.project_description_label.setText(project['description']) else: self.project_description_label.setText( self.tr(project['name'].encode('utf-8'))) if project['urls']: project_urls = '' for url in project['urls']: project_urls += url + ' \n' if not project_urls.isspace(): project_urls = project_urls[:-2] self.project_url_label.setText(project_urls) else: self.project_url_label.setText('-') else: self.project_url_label.setText('-') if project['contacts']: project_contacts = '' for contact in project['contacts']: project_contacts += contact['name'] if 'email' in contact and contact['email']: project_contacts += ', ' + contact['email'] if 'tel' in contact and contact['tel']: project_contacts += ', ' + contact['tel'] project_contacts += ' \n' if project_contacts: self.set_enabled_add_contact_label(True) self.current_contacts = project['contacts'] project_contacts = project_contacts[:-2] self.contact_information_label.setText(project_contacts) else: self.contact_information_label.setText('-') self.set_enabled_add_contact_label(False) if project['access']: self.privacy_status_label.setText(project['access'].title())
def validate_step(self): """Check if the step is valid. :returns: Tuple of validation status and error message if any :rtype: ( bool, str ) """ error_message = '' if not self.selected_project(): error_message += tr('Missing project.') return (error_message == '', error_message)
def content(): """Helper method that returns just the content. This method was added so that the text could be reused in the other contexts. :returns: A message object without brand element. :rtype: safe.messaging.message.Message """ message = m.Message() message.add(m.Paragraph(tr( 'Options will help you redefine url of Cadasta that is used as ' 'source. And also it create a credential to be used on submit ' 'new or updated projects.'))) message.add(m.Paragraph(tr( 'There are 3 input that all of that are required.'))) bullets = m.BulletedList() bullets.add(m.Text( m.ImportantText(tr('Cadasta URL')), tr('- overwrite current url as cadasta source.' 'default is https://platform-staging-api.cadasta.org/') )) bullets.add(m.Text( m.ImportantText(tr('Cadasta Username')), tr('- username that will be used for other request, e.g: create ' 'project') )) bullets.add(m.Text( m.ImportantText(tr('Cadasta Password')) )) message.add(bullets) message.add(m.Paragraph(tr( 'Fill out the form with your username and password. Click \'Connect\' ' 'button ' 'to login. If that is successful click the \'Save\' button to save ' 'the settings.'))) message.add(m.ImportantText(tr('Note that your password is not saved.'))) return message
def content(): """Helper method that returns just the content. This method was added so that the text could be reused in the other contexts. :returns: A message object without brand element. :rtype: safe.messaging.message.Message """ message = m.Message() message.add( m.Paragraph( tr('You can find updated documentation and suggested workflows ' 'on our main ' 'documentation pages: <a href="https://docs.cadasta.org/en/' '11-qgis-plugin.html">QGIS chapter</a>. (requires internet ' 'access to view)'))) message.add( m.Paragraph( tr('There are three windows that will help you ' 'to manage your project\'s data.'))) bullets = m.BulletedList() bullets.add(m.Text(m.ImportantText(tr('Download Project')))) bullets.add(m.Text(m.ImportantText(tr('Create Project')))) bullets.add(m.Text(m.ImportantText(tr('Update Project')))) message.add(bullets) message.add( m.Paragraph( tr('Use the <b>User Settings</b> window to log in to your account ' 'and get started!' ''))) return message
def upload_questionnaire_project(self): """Upload questionnaire.""" self.set_status(tr('Update questionnaire')) post_url = '/api/v1/organizations/' \ '%s/projects/%s/questionnaire/' % ( self.data['organisation']['slug'], self.project_upload_result['slug'] ) post_data = QByteArray() post_data.append(self.data['questionnaire']) connector = ApiConnect(get_url_instance() + post_url) status, result = self._call_json_put(connector, post_data) self.set_status(tr('Finished update questionnaire')) if status: self.set_progress_bar(self.current_progress + self.upload_increment) else: self.set_progress_bar(0) self.set_status(Utilities.extract_error_detail(result))
def add(self, text): """add a Text MessageElement to the existing Text Strings can be passed and are automatically converted in to item.Text() :param text: An element to add to the text. :type text: str """ if self._is_stringable(text) or self._is_qstring(text): self.text.append(PlainText(text)) elif isinstance(text, Text): self.text.append(text) elif isinstance(text, QPyNullVariant): self.text.append( PlainText( tr('Null (PyQt4.QtCore.QPyNullVariant) found from the data.' ))) elif text is None: self.text.append(PlainText( tr('None or Null found from the data.'))) else: raise InvalidMessageItemError(text, text.__class__)
def clear_information(self): """Clear login information.""" self.username_input.setReadOnly(False) self.password_input.setReadOnly(False) self.username_input.setStyleSheet("") self.password_input.setStyleSheet("") self.username_input.clear() self.password_input.clear() self.ok_label.clear() delete_authtoken() delete_setting('username') self.test_connection_button.setEnabled(True) self.token_status.setText(tr('Auth token is empty.')) self.unauthenticated.emit()
def _render(self): """Create a Message version of this ErrorMessage Args: none Returns: the Message instance of this ErrorMessage Raises: Errors are propagated """ message = Message() message.add(Heading(tr('Problem'), **PROBLEM_STYLE)) message.add( Paragraph( tr('The following problem(s) were encountered whilst running the ' 'analysis.'))) items = BulletedList() for p in reversed(self.problems): # p is _always_ not None items.add(p) message.add(items) message.add(Heading(tr('Suggestion'), **SUGGESTION_STYLE)) message.add( Paragraph(tr('You can try the following to resolve the issue:'))) if len(self.suggestions) < 1: suggestions = self.standard_suggestions() message.add(suggestions) else: items = BulletedList() for s in reversed(self.suggestions): if s is not None: items.add(s) message.add(items) if len(self.details) > 0: items = BulletedList() message.add(Heading(tr('Details'), **DETAILS_STYLE)) message.add( Paragraph( tr('These additional details were reported when the problem ' 'occurred.'))) for d in self.details: if d is not None: items.add(d) message.add(items) message.add(Heading(tr('Diagnostics'), **TRACEBACK_STYLE)) message.add(self.tracebacks) return message
def set_widgets(self): """Set all widgets on the tab.""" if not self.layer or self.layer != self.parent.layer: self.layer = self.parent.layer self.set_attributes_box() self.advanced_box.setVisible(False) self.advanced_button.mousePressEvent = self.toogled_advanced_area self.questionnaire_button.clicked.connect( self.show_questionnaire ) self.check_questionnaire() self.label.setToolTip( tr('Location type field is required please select.')) # Set tab focus self.location_type_box.setFocus()
def set_attributes_box(self): """Set all attribute box widgets.""" field_names = [field.name() for field in self.layer.pendingFields()] self.layer_attributes = [] for elem in self.layer.getFeatures(): self.layer_attributes.append( (dict(zip(field_names, elem.attributes()))) ) field_names.append('--------- {field} ----------'.format( field=tr('No field'))) self.set_items_combo_box(self.location_type_box, field_names) self.set_items_combo_box(self.party_name_box, field_names) self.set_items_combo_box(self.relationship_type_box, field_names) self.set_items_combo_box(self.party_type_box, field_names)
def emit(self, record): """Try to log the message to QGIS if available, otherwise do nothing. :param record: logging record containing whatever info needs to be logged. """ try: # Check logging.LogRecord properties for lots of other goodies # like line number etc. you can get from the log message. QgsMessageLog.logMessage(record.getMessage(), 'CadastaQGISPlugin', 0) except MemoryError: message = tr( 'Due to memory limitations on this machine, Cadasta can not ' 'handle the full log') print message QgsMessageLog.logMessage(message, 'CadastaQGISPlugin', 0)
def __init__(self, parent=None, iface=None): """Constructor for the dialog. .. note:: In QtDesigner the advanced editor's predefined keywords list should be shown in english always, so when adding entries to cboKeyword, be sure to choose :safe_qgis:`Properties<<` and untick the :safe_qgis:`translatable` property. :param parent: Parent widget of this dialog. :type parent: QWidget :param iface: QGIS QGisAppInterface instance. :type iface: QGisAppInterface """ super(ProjectCreationWizard, self).__init__(parent, iface) self.set_subtitle(tr('Cadasta project creation wizard')) self.layer = None
def check_requirement(self): """Checking attributes that required. :return: Is missed :rtype: bool """ requirement_miss = False for location in self.data['locations']['features']: try: location['fields']['location_type'] except KeyError: self.set_progress_bar(0) self.set_status( Utilities.extract_error_detail( tr('Location_type is not found in attribute. ' 'Please update before uploading again.'))) requirement_miss = True break return requirement_miss
def __init__(self, parent=None, iface=None): """Constructor for the dialog. .. note:: In QtDesigner the advanced editor's predefined keywords list should be shown in english always, so when adding entries to cboKeyword, be sure to choose :safe_qgis:`Properties<<` and untick the :safe_qgis:`translatable` property. :param parent: Parent widget of this dialog. :type parent: QWidget :param iface: QGIS QGisAppInterface instance. :type iface: QGisAppInterface """ QDialog.__init__(self, parent) self.setupUi(self) self.setWindowTitle(tr('About Cadasta %s' % get_plugin_version())) self.iface = iface # set the helpers self.show_cadasta_about()
def __init__(self, parent=None, iface=None, title='Cadasta', subtitle='', widget=None): """Constructor for the dialog. .. note:: In QtDesigner the advanced editor's predefined keywords list should be shown in english always, so when adding entries to cboKeyword, be sure to choose :safe_qgis:`Properties<<` and untick the :safe_qgis:`translatable` property. :param parent: Parent widget of this dialog. :type parent: QWidget :param iface: QGIS QGisAppInterface instance. :type iface: QGisAppInterface :param title: Title of dialog. :type title: str :param subtitle: Subtitle of dialog. :type subtitle: str :param widget: Widget that will be rendered to dialog :type widget: WidgetBase """ QDialog.__init__(self, parent) self.setupUi(self) self.setWindowTitle(title) self.label_subtitle.setText( tr(subtitle) ) self.iface = iface self.set_logo() self.widget = widget if self.widget: self.widget.parent = self self.socket_layout.addWidget(self.widget)
def upload_locations(self, update_questionnaire_attribute): """Upload project locations to cadasta. :param update_questionnaire_attribute: Boolean to check if it need to upload the attributes :type update_questionnaire_attribute: bool """ self.set_status(tr('Uploading locations')) total_locations = len(self.data['locations']['features']) progress_left = \ (100 - self.current_progress - self.upload_increment) \ / total_locations post_url = '/api/v1/organizations/%s/projects/%s/spatial/' % ( self.data['organisation']['slug'], self.project_upload_result['slug']) failed = 0 try: questionnaire = json.loads(self.questionnaire) except ValueError: questionnaire = {} for location in self.data['locations']['features']: post_data = { 'geometry': location['geometry'], 'type': location['fields']['location_type'] } if update_questionnaire_attribute: if location['properties']: post_data['attributes'] = location['properties'] for key, value in post_data['attributes'].iteritems(): # Set None to empty string if not value: post_data['attributes'][key] = u'' if 'id' in post_data['attributes']: del post_data['attributes']['id'] for question in \ questionnaire['question_groups'][0]['questions']: if question['name'] not in post_data['attributes']: post_data['attributes'][question['name']] = u'' connector = ApiConnect(get_url_instance() + post_url) post_data = Utilities.json_dumps(post_data) status, result = self._call_json_post(connector, post_data) if status: self.set_progress_bar(self.current_progress + progress_left) try: result_obj = result if 'properties' in result_obj: location['spatial_id'] = result_obj['properties']['id'] except ValueError as e: LOGGER.exception('message') else: self.set_progress_bar(0) self.set_status(Utilities.extract_error_detail(result)) failed += 1 self.set_progress_bar(100) if failed == 0: self.set_status(tr('Finished uploading all locations')) else: self.set_status(tr('Finish with %d failed' % failed))