def send_email(recipient_email, email_template, email_params, email_subject, non_html_message, sender_email=None): if not sender_email: if Settings.get_by_name("APP_EMAIL"): sender_email = Settings.get_by_name( "APP_EMAIL").value # reads from settings else: sender_email = "*****@*****.**" # send web app URL data by default to email template email_params["app_root_url"] = request.url_root # render the email HTML body email_body = render_template(email_template, **email_params) # params sent to the background task payload = { "recipient_email": recipient_email, "email_subject": email_subject, "sender_email": sender_email, "email_body": email_body, "non_html_message": non_html_message } run_background_task( relative_path=url_for("tasks.send_email_task.send_email_via_sendgrid"), payload=payload, queue="email", project=os.environ.get("GOOGLE_CLOUD_PROJECT"), location="europe-west1")
def post(self, category, command): settings = Settings.get_instance() items = settings[category] if command == "add": value = self.must_get("value") new_id = items.add(value) self.response.write(str(new_id)) elif command == "edit": id = int(self.must_get("id")) value = self.must_get("value") success = items.edit(id, value) if success: self.response.write("OK") else: self.abort(400, "ID %d doesn't exist." % id) elif command == "remove": id = int(self.must_get("id")) success = items.remove(id) if success: self.response.write("OK") else: self.abort(400, "ID %d doesn't exist." % id) else: self.abort(500, "Invalid command. Should not occur.") Settings.save()
def __init__(self, app): super().__init__() self.app = app self.showMaximized() self.image_service = ImageService() self.storage_service = StorageService() self.stacked_widget = QStackedWidget() self.stacked_widget.showMaximized() self.main_widget = MainWidget(self) self.settings_widget = SettingsWidget(self, Settings()) self.pattern_widget = PatternWidget(self, Pattern()) self.stacked_widget.addWidget(self.main_widget) self.stacked_widget.addWidget(self.pattern_widget) self.stacked_widget.addWidget(self.settings_widget) self.setCentralWidget(self.stacked_widget) self.selected_pattern_name = None self.selected_settings_name = None self.pattern = None self.settings = None self._create_actions() self._set_menu_bar() self._set_tool_bar() self.errors_color = QColor(255, 0, 0)
def get(self): settings = Settings.get() #Force email address update... posts = Post.query().order(-Post.date).fetch(1) is_newest = True if posts: post = posts[0] is_oldest = post.date == Post.min_date() else: post = None is_oldest = True #See if this is the very first time we've been here. In that case #send an email immediately to get people started... first_time = False if not Slug.query().get() and not Post.query().get(): first_time = True DailyMail().send(True) self.response.write( get_template('frontpage.html').render({ "page": "frontpage", "post": post, "is_oldest": is_oldest, "is_newest": is_newest, "first_time": first_time, "email": settings.email_address }))
def get(self): settings = Settings.get() #Force email address update... posts = Post.query().order(-Post.date).fetch(1) is_newest = True if posts: post = posts[0] is_oldest = post.date == Post.min_date() else: post = None is_oldest = True #See if this is the very first time we've been here. In that case #send an email immediately to get people started... first_time = False if not Slug.query().get() and not Post.query().get(): first_time = True DailyMail().send(True) self.response.write(get_template('frontpage.html').render( { "page":"frontpage", "post":post, "is_oldest" : is_oldest, "is_newest" : is_newest, "first_time" : first_time, "email" : settings.email_address }))
class SerialManager: serial = None settings = Settings.singleton() def __init__(self): self.serial = Serial(port=self.settings.serial_dev, baudrate=1200, parity=PARITY_NONE, stopbits=STOPBITS_ONE, bytesize=SEVENBITS, timeout=1) Logging.info("Teleinfo is reading on " + self.settings.serial_dev + "...") Logging.info("Search the beginning of the trame") self.search_beginning_trame() def search_beginning_trame(self): line = self.serial.readline() while b'\x02' not in line: # search the beginning trame character line = self.serial.readline() def read_line(self): return self.serial.readline().decode("utf-8") def close(self): self.serial.close() self.serial = None
def func_wrapper(self, *args): user = users.get_current_user() if user and user.email() in Settings.get_instance( )["emails"].values.values(): return func(self, *args) self.response.set_status(403) self.response.write( 'Move along, nothing to do here! Move along...<br><a href="%s">Logout</a>' % users.create_logout_url("/"))
def post(self): settings = Settings.get() settings.email_address = self.request.get('email-address') settings.timezone = self.request.get('timezone') settings.email_hour = int(self.request.get('email-hour')) settings.dropbox_access_token = self.request.get('dropbox-access-token') settings.include_old_post_in_entry = self.request.get('include-old-entry') == 'yes' settings.put() self._render(settings, True)
def is_local(): setting = Settings.get_by_name("PROD_ENV") if not setting: return True if setting.value: return False else: return True
def load_settings(self, settings_name): settings = None settings_path = os.path.join(SETTINGS_DIR, settings_name) if os.path.exists(settings_path): with open(settings_path, 'r') as f: json_dict = json.load(f) image_processes = [ ImageProcess.from_json(j) for j in json_dict[IMAGE_PROCESSES] ] settings = Settings(settings_name, image_processes) return settings
def __init__(self): """ Initialize Employee class """ self.model = { "name": "employees", "id": "employee_id", "fields": ("employee_id", "salesrep", "fullname", "email", "country", "sas"), "types": ("INTEGER PRIMARY KEY NOT NULL", "TEXT", "TEXT", "TEXT", "TEXT", "INTEGER DEFAULT 0") } self._employee = {} self.q = Query() if not self.q.exist_table(self.model["name"]): sql = self.q.build("create", self.model) self.q.execute(sql) self.s = Settings() if rules.check_settings(self.s.settings): self.load(self.s.settings["usermail"])
def send_email_via_sendgrid(): """A background task that sends an email via SendGrid.""" data = json.loads(request.get_data(as_text=True)) recipient_email = data.get("recipient_email") sender_email = data.get("sender_email") email_subject = data.get("email_subject") email_body = data.get("email_body") non_html_message = data.get("non_html_message") if is_local(): # localhost (not really sending the email) logging.warning("***********************") logging.warning( "You are on localhost, so no e-mail will be sent. This is message:" ) logging.warning("Recipient: " + recipient_email) logging.warning("Sender: " + sender_email) logging.warning("Subject: " + email_subject) logging.warning("Body: " + non_html_message) logging.warning("+++++++++++++++++++++++") return "{sender_email} {email_subject}".format( sender_email=sender_email, email_subject=email_subject) else: # production (sending the email via SendGrid) if request.headers.get("X-AppEngine-QueueName"): # If the request has this header (X-AppEngine-QueueName), then it really came from Google Cloud Tasks. # Third-party requests that contain headers started with X are stripped of these headers once they hit GAE # servers. That's why no one can fake these headers. # SendGrid setup sg_api_key = Settings.get_by_name("SendGrid-Mail") sg = SendGridAPIClient(api_key=sg_api_key.value) # Set up email message email_message = Mail(from_email=sender_email, to_emails=recipient_email, subject=email_subject, html_content=email_body) try: response = sg.send(email_message) logging.info(response.status_code) logging.info(response.body) logging.info(response.headers) except Exception as e: logging.error(str(e)) return "true"
def _new_settings_action_handler(self): file_dialog = QFileDialog(self) file_dialog.setNameFilters(["All Files (*)", "Images (*.png *.jpg)"]) file_dialog.selectNameFilter("Images (*.png *.jpg)") if file_dialog.exec_(): filename = file_dialog.selectedFiles()[0] with wait_cursor(): image_orig = cv2.imread(filename, cv2.IMREAD_COLOR) self.stacked_widget.removeWidget(self.settings_widget) self.settings_widget = SettingsWidget(self, Settings()) self.stacked_widget.addWidget(self.settings_widget) self.stacked_widget.setCurrentWidget(self.settings_widget) self.settings_widget.setImage(image_orig) self.settings_widget.update()
def log_error(subject, message, *args): if args: try: message = message % args except: pass logging.error(subject + ' : ' + message) subject = 'MyLife Error: ' + subject app_id = app_identity.get_application_id() sender = "MyLife Errors <errors@%s.appspotmail.com>" % app_id try: to = Settings.get().email_address mail.check_email_valid(to, 'To') mail.send_mail(sender, to, subject, message) except: mail.send_mail_to_admins(sender, subject, message)
def get(self): #Check whether the migration is done so we can see whether to show the Blobstore Migration #or not... settings = Settings.get() if not settings.blobstore_migration_done: migration_task_finished = bool(MigrateTask.query(MigrateTask.status == 'finished').get()) if migration_task_finished: settings.blobstore_migration_done = True settings.put() else: #Try to figure out whether this is a new user that has nothing in the blobstore... if not UserImage.query().get(): settings.blobstore_migration_done = True settings.put() self._render(settings)
class Teleinfo: serial_manager = None settings = Settings.singleton() database = settings.database def run(self): try: nb_error = 0 self.serial_manager = SerialManager() # lecture de la première ligne de la première trame line = self.serial_manager.read_line() Logging.info("Reading data from teleinfo") consumption = None last_consumption = None while True: ar = line.split(" ") try: key = Keyword.value_of(ar[0]) if key is None: line = self.serial_manager.read_line() continue if key == Keyword.ADCO: # Begining of block if consumption is not None: if last_consumption is None or not last_consumption.has_same_indexes( consumption) or (consumption.datetime - last_consumption.datetime ).seconds > 30: self.database.commit_model(consumption) self.settings.remove_error_file() nb_error = 0 last_consumption = consumption consumption = models.Consumption() elif consumption is None: Logging.warning( "We search the beginning of first trame") line = self.serial_manager.read_line() continue if key.is_int_value(): value = int(ar[1]) else: value = ar[1] if key == Keyword.HCHP: consumption.index_hp = value elif key == Keyword.HCHC: consumption.index_hc = value elif key == Keyword.IINST: consumption.intensite_inst = value elif key == Keyword.PAPP: consumption.puissance_apparente = value elif key == Keyword.PTEC: if value == "HC..": consumption.periode = 2 except Exception as e: nb_error = nb_error + 1 if nb_error > 20: Logging.error("Too many error. Stop the service.") sys.exit() try: Logging.error("Exception no " + str(nb_error) + ": %s" % e) self.settings.create_error_file() self.settings.init_db() except Exception as e2: Logging.error("Exception : %s" % e2) line = self.serial_manager.read_line() finally: if self.serial_manager is not None: self.serial_manager.close()
def get(self): images_total = 0 images_backed_up = 0 try: self.response.headers['Content-Type'] = 'text/plain' settings = Settings.get() if not settings.dropbox_access_token: self.log( 'No access token available, no backup will be performed.') return posts = [p for p in Post.query().order(Post.date).fetch()] self.log('Backing up %s posts to Dropbox' % len(posts)) post_text = StringIO() for p in posts: post_text.write(p.date.strftime('%Y-%m-%d')) post_text.write('\r\n\r\n') post_text.write( p.text.replace('\r\n', '\n').replace('\n', '\r\n').rstrip()) post_text.write('\r\n\r\n') result = self.put_file(settings.dropbox_access_token, 'MyLife.txt', post_text.getvalue().encode('utf-8')) post_text.close() self.log('Backed up posts. Revision: %s' % result['rev']) self.log('Fetching Dropbox file list') files_in_dropbox = self.get_dropbox_filelist( settings.dropbox_access_token) self.log('Got %s files from Dropbox' % len(files_in_dropbox)) self.log('Fetching images...') images = [ i for i in UserImage.query().order(UserImage.date).fetch() ] self.log('Total images in MyLife: %s' % len(images)) not_backed_up = [i for i in images if not i.backed_up_in_dropbox] not_in_dropbox = [ i for i in images if not i.filename in files_in_dropbox ] self.log('\nFiles not backed up: \n\n' + '\n'.join([i.filename for i in not_backed_up])) self.log('\nFiles marked as backed up, but not in Dropbox: \n\n' + '\n'.join([i.filename for i in not_in_dropbox])) images = not_backed_up + not_in_dropbox images_total = len(images) self.log('Found %s images that need to be backed up in Dropbox' % images_total) for img in images: self.log('Backing up %s' % img.filename) bytes = filestore.read(img.original_size_key) result = self.put_file(settings.dropbox_access_token, img.filename, bytes) self.log('Backed up %s. Revision: %s' % (img.filename, result['rev'])) img.backed_up_in_dropbox = True img.put() images_backed_up += 1 settings.dropbox_last_backup = datetime.datetime.now() settings.put() self.log('Finished backup successfully') except apiproxy_errors.OverQuotaError, ex: self.log(ex) log_error( 'Error backing up to Dropbox, quota exceeded', 'The backup operation did not complete because it ran out of quota. ' + 'The next time it runs it will continue backing up your posts and images.' + '%s images out of %s were backed up before failing' % (images_backed_up, images_total))
class Employee: """ Employee class """ def __init__(self): """ Initialize Employee class """ self.model = { "name": "employees", "id": "employee_id", "fields": ("employee_id", "salesrep", "fullname", "email", "country", "sas"), "types": ("INTEGER PRIMARY KEY NOT NULL", "TEXT", "TEXT", "TEXT", "TEXT", "INTEGER DEFAULT 0") } self._employee = {} self.q = Query() if not self.q.exist_table(self.model["name"]): sql = self.q.build("create", self.model) self.q.execute(sql) self.s = Settings() if rules.check_settings(self.s.settings): self.load(self.s.settings["usermail"]) @property def employee(self): """ Return current and only employeeid """ return self._employee def insert(self, values): """ Insert employee in database Args: values: """ sql = self.q.build("insert", self.model) self.q.execute(sql, values=values) def load(self, email): """ Load the employee """ filters = [("email", "=")] values = (email, ) sql = self.q.build("select", self.model, filters=filters) success, data = self.q.execute(sql, values) # first check if employee is loaded # second check is in exception handling try: _ = data[0] self._employee = dict(zip(self.model["fields"], data[0])) except IndexError: if httpFn.inet_conn_check(): # load from http self.load_from_http() success, data = self.q.execute(sql, values) try: # second check after load_from_http _ = data[0] self._employee = dict(zip(self.model["fields"], data[0])) except IndexError: self._employee = {} def load_from_http(self): """ Load employee from http """ self.s.get() data = httpFn.get_employee_data(self.s) if data: data = list(data) data[0:0] = [None] self.insert(tuple(data)) def update(self): """ Update employee in database """ fields = list(self.model["fields"])[1:] filters = [(self.model["id"], "=")] values = self.q.values_to_update(self._employee.values()) sql = self.q.build("update", self.model, update=fields, filters=filters) self.q.execute(sql, values=values)
async def updateSettings(request): params = await request.json() settings = Settings(params['frameBufferSize'], params['videoPath']) db.update_settings(settings) return web.Response(status=201)
def get(self, year, month): Settings.get() #Force email address update... now = datetime.datetime.now() if not year: last_post = Post.query().order(-Post.date).get() if last_post: year, month = last_post.date.year, last_post.date.month else: year, month = now.year, now.month else: year, month = int(year), int(month) from_date = datetime.date(year, month, 1) to_month = month + 1 to_year = year if to_month == 13: to_month = 1 to_year += 1 to_date = datetime.date(to_year, to_month, 1) posts = [p for p in Post.query(ndb.AND(Post.date >= from_date, Post.date < to_date)).order(-Post.date).fetch()] month_name = from_date.strftime('%B %Y') #Get month list months = PostCounter.get().months[:] def cmp_months(a,b): if a.year != b.year: return cmp(a.year, b.year) else: return cmp(a.month, b.month) months.sort(cmp_months) archive = [] next_link, prev_link = None, None for i, m in enumerate(months): date = datetime.date(m.year, m.month,1) descr = '%s, %s posts' % (date.strftime('%B %Y'), m.count) value = date.strftime('%Y-%m') archive.append((value,descr, m.year == year and m.month == month)) if m.year == year and m.month == month: if i != 0: prev_link = '/past/%s' % datetime.date(months[i-1].year, months[i-1].month, 1).strftime('%Y-%m') if i < len(months)-1: next_link = '/past/%s' % datetime.date(months[i+1].year, months[i+1].month, 1).strftime('%Y-%m') if not archive: archive.append(('', '%s, 0 posts' % now.strftime('%B %Y'), False)) data = { "page" : "past", "posts" : posts, "month" : month_name, "archive" : archive, "next" : next_link, "prev" : prev_link } self.response.write(get_template('past.html').render(data))
STATUS_REBALANCE = 'rebalance' STATUS_RIDES = 'rides' STATUS_NEXT_CYCLE = 'next-cycle' STATUS_FINISH = 'finish' STATUS_NONE = None PREDICTION_MODE_7DMA = 'Past 7 Days Moving Average' PREDICTION_MODE_LSTM = 'LSTM' PREDICTION_MODE_GRU = 'GRU' PREDICTION_MODE_BI_LSTM = 'Bi-LSTM' PREDICTION_MODE_ACTUAL = 'ACTUAL' START_TIME = datetime(year=2018, month=10, day=1, hour=0) DEFAULT_SETTINGS = Settings(interval_hour=2, peak_cost=2, off_peak_cost=1, budget_per_cycle=1500, cost_coef=0.2, prediction_mode=PREDICTION_MODE_7DMA) MOVING_AVERAGE_DAYS = 7 RESULTS_PATH = 'results' check_dir(RESULTS_PATH) DATA_PATH = 'data' JOURNEYS_DATA_PATH = os.path.join( DATA_PATH, 'london_journeys_count_with_2h_interval.csv') STATIONS_DATA_PATH = os.path.join(DATA_PATH, 'london_stations.json') PREDICTION_DATA_PATHS = { PREDICTION_MODE_7DMA: os.path.join(DATA_PATH, 'london_journeys_predict_with_2h_interval_7DMA.csv'), PREDICTION_MODE_LSTM:
class MainWindow(QMainWindow, Ui_mainWindow): """ Main Application Window """ def __init__(self, parent=None): """ Initialize MainWindow class """ super(MainWindow, self).__init__(parent) self.setupUi(self) thread = QThread() thread.currentThread().setObjectName(__appname__) configfn.check_config_folder() # Check appdata folder in users home self.textWorkdate.setText(datetime.date.today().isoformat() ) # initialize workdate to current date self._archivedOrderlines = OrderLine() # Initialize Detail object self._archivedVisits = Visit() # Initialize Visit object self._contacts = Contact() # Initialize Contact object self._customers = Customer() # Initialize Customer object self._employees = Employee() # Initialize Employee object self._orderLines = OrderLine() self._products = Product() # Initialize Product object self._reports = Report() # Initialize Report object self._settings = Settings() # Initialize Settings object self._visits = Visit() self.buttonArchiveContacts.clicked.connect(self.archive_contacts) self.buttonArchiveCustomer.clicked.connect(self.archive_customer) self.buttonArchiveVisit.clicked.connect(self.archive_visit) self.buttonCreateContact.clicked.connect(self.create_contact) self.buttonCreateCustomer.clicked.connect(self.create_customer) self.buttonCreateReport.clicked.connect(self.create_report) self.buttonCreateVisit.clicked.connect(self.load_visit) self.buttonGetCustomers.clicked.connect(self.get_customers) self.buttonGetPricelist.clicked.connect(self.get_pricelist) self.toolButtonArchiveSettings.clicked.connect(self.archive_settings) self.toolButtonCustomer.clicked.connect(self.show_page_customer) self.toolButtonCustomers.clicked.connect(self.show_page_customers) self.toolButtonCustomerVisits.clicked.connect( self.show_page_customer_visits) self.toolButtonExit.clicked.connect(self.app_exit) self.toolButtonInfo.clicked.connect(self.show_page_info) self.toolButtonPricelist.clicked.connect(self.show_page_pricelist) self.toolButtonReport.clicked.connect(self.show_page_report) self.toolButtonReports.clicked.connect(self.show_page_reports) self.toolButtonSettings.clicked.connect(self.show_page_settings) self.toolButtonCustomerVisit.clicked.connect(self.show_page_visit) self.toolButtonDeleteSalesData.clicked.connect(self.zero_database) self.toolButtonExportDatabase.clicked.connect(self.data_export) self.toolButtonImportCsvData.clicked.connect( self.show_csv_import_dialog) self.toolButtonImportDatabase.clicked.connect(self.data_import) self.widgetCustomers.currentItemChanged.connect( self.on_customer_changed) self.widgetCustomers.itemDoubleClicked.connect( self.on_customer_double_clicked) self.widgetArchivedVisits.currentItemChanged.connect( self.on_visit_changed) self.widgetArchivedVisits.setColumnHidden(0, True) self.widgetArchivedOrderLines.setColumnWidth(0, 30) self.widgetArchivedOrderLines.setColumnWidth(1, 30) self.widgetArchivedOrderLines.setColumnWidth(2, 100) self.widgetArchivedOrderLines.setColumnWidth(3, 150) self.widgetArchivedOrderLines.setColumnWidth(4, 60) self.widgetArchivedOrderLines.setColumnWidth(5, 40) self.widgetCustomers.setColumnHidden(0, True) # ID self.widgetCustomers.setColumnWidth(1, 100) self.widgetCustomers.setColumnWidth(2, 100) self.widgetCustomers.setColumnWidth(3, 100) self.widgetCustomers.setColumnWidth(4, 250) self.widgetCustomers.setColumnWidth(5, 60) self.widgetPricelist.setColumnWidth(0, 70) self.widgetPricelist.setColumnWidth(1, 100) self.widgetPricelist.setColumnWidth(2, 150) self.widgetPricelist.setColumnWidth(3, 50) self.widgetPricelist.setColumnWidth(4, 50) self.widgetPricelist.setColumnWidth(5, 50) self.widgetPricelist.setColumnWidth(6, 50) self.widgetPricelist.setColumnWidth(7, 50) self.widgetPricelist.setColumnWidth(8, 50) self.widgetPricelist.setColumnWidth(9, 50) self.widgetPricelist.setColumnWidth(10, 50) self.widgetPricelist.setColumnWidth(11, 50) self.widgetPricelist.setColumnWidth(12, 50) self.widgetReports.setColumnHidden(0, True) # ID self.widgetReports.setColumnWidth(1, 80) # rep_date self.widgetReports.setColumnWidth(2, 60) # visits self.widgetReports.setColumnWidth(3, 60) # sale day self.widgetReports.setColumnWidth(4, 60) # demo day self.widgetReports.setColumnWidth(5, 100) # turnover day self.widgetReports.setColumnWidth(6, 50) # km # self.widgetReports column 7 # supervisor self.widgetReportVisits.setColumnWidth(0, 150) self.widgetReportVisits.setColumnWidth(1, 100) self.widgetReportVisits.setColumnWidth(2, 100) self.widgetReportVisits.setColumnWidth(3, 60) self.populate_customer_list() self.populate_price_list() try: cid = self._settings.settings["cust_idx"] if self._customers.lookup_by_id(cid): try: self.widgetCustomers.setCurrentIndex( self.widgetCustomers.indexFromItem( self.widgetCustomers.findItems(str( self._customers.customer["customer_id"]), Qt.MatchExactly, column=0)[0])) self.toolButtonCustomer.click() except KeyError: pass except KeyError: return self._reports.load(workdate=self.textWorkdate.text()) self.populate_report_list() self.populate_report_visit_list() self.toolButtonReport.click() def closeEvent(self, event): """ Slot for close event signal Args: event: intended use is warning about unsaved data """ # TODO handle close event self.app_exit() def display_sync_status(self): """ Update status fields """ try: self.textCustomerLocalDate.setText(self._settings.settings["lsc"]) self.textPricelistLocalDate.setText(self._settings.settings["lsp"]) self.textCustomerServerDate.setText(self._settings.settings["sac"]) self.textPricelistServerDate.setText( self._settings.settings["sap"]) except KeyError: pass def populate_archived_visit_details(self): """ Populate the details list based on the line visit """ self.widgetArchivedOrderLines.clear() self.labelArchivedApprovedText.setText("") self.labelArchivedSendText.setText("") self.textArchivedOrderPoNumber.setText("") self.textArchivedOrderSale.setText("") self.textArchivedOrderSas.setText("") self.textArchivedOrderTotal.setText("") self.textArchivedVisitNote.setText("") items = [] try: self.labelArchivedSendText.setText( utils.bool2dk( utils.int2bool(self._archivedVisits.visit["po_sent"]))) self.labelArchivedApprovedText.setText( utils.bool2dk( utils.int2bool(self._archivedVisits.visit["po_approved"]))) self.textArchivedOrderPoNumber.setText( self._archivedVisits.visit["po_number"]) self.textArchivedOrderSale.setText( str(self._archivedVisits.visit["po_sale"])) self.textArchivedOrderSas.setText( str(self._archivedVisits.visit["po_sas"])) self.textArchivedOrderTotal.setText( str(self._archivedVisits.visit["po_total"])) self.textArchivedVisitNote.setText( self._archivedVisits.visit["visit_note"]) self._archivedOrderlines.list_ = self._archivedVisits.visit[ "visit_id"] for line in self._archivedOrderlines.list_: item = QTreeWidgetItem([ line["linetype"], str(line["pcs"]), line["sku"], line["text"], str(line["price"]), str(line["discount"]), line["linenote"] ]) items.append(item) except KeyError: pass except IndexError: pass self.widgetArchivedOrderLines.addTopLevelItems(items) def populate_archived_visits(self): """ Populate the visitlist based on the active customer """ self.widgetArchivedVisits.clear() items = [] try: self._archivedVisits.list_by_customer( self._customers.customer["customer_id"]) for visit in self._archivedVisits.visits: item = QTreeWidgetItem([ str(visit["visit_id"]), visit["visit_date"], visit["po_buyer"], visit["prod_demo"], visit["prod_sale"], visit["po_note"] ]) items.append(item) if visit["visit_date"] == self.textWorkdate.text(): self.toolButtonCustomerVisit.setEnabled(True) except IndexError: pass except KeyError: pass self.widgetArchivedVisits.addTopLevelItems(items) def populate_contact_list(self): """ Populate the contactlist based on currently selected customer """ # load contacts self.widgetCustomerContacts.clear() items = [] try: self._contacts.list_ = self._customers.customer["customer_id"] for c in self._contacts.list_: item = QTreeWidgetItem( [c["name"], c["department"], c["phone"], c["email"]]) items.append(item) except IndexError: pass except KeyError: pass self.widgetCustomerContacts.addTopLevelItems(items) def populate_customer_list(self): """ Populate customer list """ self.widgetCustomers.clear() # shake the tree for leaves items = [] # temporary list try: for c in self._customers.customers: item = QTreeWidgetItem([ str(c["customer_id"]), c["account"], c["phone1"], c["phone2"], c["company"], c["zipcode"], c["city"] ]) items.append(item) except (IndexError, KeyError): pass # assign Widgets to Tree self.widgetCustomers.addTopLevelItems(items) self.widgetCustomers.setSortingEnabled(True) # enable sorting def populate_price_list(self): """ Populate widgetPricelist """ self.widgetPricelist.clear() pricelist = [] try: for product in self._products.products: item = QTreeWidgetItem([ product["item"], product["sku"], product["name1"], str(product["price"]).format("#.##"), str(product["d2"]).format("#.##"), str(product["d4"]).format("#.##"), str(product["d6"]).format("#.##"), str(product["d8"]).format("#.##"), str(product["d12"]).format("#.##"), str(product["d24"]).format("#.##"), str(product["d48"]).format("#.##"), str(product["d96"]).format("#.##"), str(product["net"]).format("#.##") ]) pricelist.append(item) except IndexError as i: print("IndexError: {}".format(i)) except KeyError as k: print("KeyError: {}".format(k)) self.widgetPricelist.addTopLevelItems(pricelist) self.widgetPricelist.setSortingEnabled(True) def populate_report_list(self): """ Populate widgetReports """ self.widgetReports.clear() reports = [] try: for report in self._reports.reports: item = QTreeWidgetItem([ str(report["report_id"]), report["rep_date"], str(report["newvisitday"] + report["recallvisitday"]), str(report["newdemoday"] + report["recalldemoday"]), str(report["newsaleday"] + report["recallsaleday"]), str(report["newturnoverday"] + report["recallturnoverday"] + report["sasturnoverday"]), str(report["kmevening"] - report["kmmorning"]), report["supervisor"] ]) reports.append(item) except (IndexError, KeyError): pass self.widgetReports.addTopLevelItems(reports) def populate_report_visit_list(self): """ Populate widgetReportVisits """ self.widgetReportVisits.clear() items = [] try: self._visits.list_by_date(self.textWorkdate.text()) for v in self._visits.visits: c = self._customers.lookup_by_id(v["customer_id"]) if c: item = QTreeWidgetItem([ self._customers.customer["company"], v["prod_demo"], v["prod_sale"], v["po_total"] ]) items.append(item) except (IndexError, KeyError): pass self.widgetReportVisits.addTopLevelItems(items) def populate_settings_page(self): """ Populate settings page :return: """ try: self.textAppUserMail.setText(self._settings.settings["usermail"]) self.textAppUserPass.setText(self._settings.settings["userpass"]) self.textAppUserCountry.setText( self._settings.settings["usercountry"]) self.textAppDataServer.setText(self._settings.settings["http"]) self.textAppMailServer.setText(self._settings.settings["smtp"]) self.textAppMailServerPort.setText( str(self._settings.settings["port"])) self.textAppMailOrderTo.setText(self._settings.settings["mailto"]) self.checkServerData.setChecked( utils.int2bool(self._settings.settings["sc"])) self.textExtMailServer.setText( self._settings.settings["mailserver"]) self.textExtMailServerPort.setText( str(self._settings.settings["mailport"])) self.textExtMailServerUser.setText( self._settings.settings["mailuser"]) self.textExtMailServerPass.setText( self._settings.settings["mailpass"]) except KeyError: pass def resizeEvent(self, event): """ Slot for the resize event signal Args: event: intended use is resize content to window :param event: """ # TODO handle resize event # w = event.size().width() # h = event.size().height() # dpival = self.labelAvailable.devicePixelRatio() # dpivalf = self.labelAvailable.devicePixelRatioF() # dpivalfs = self.labelAvailable.devicePixelRatioFScale() # dpilogx = self.labelAvailable.logicalDpiX() # dpilogy = self.labelAvailable.logicalDpiY() # # winch = w/dpival # hinch = h/dpival # print("width = {}\n" # "height = {}\n" # "dpi = {}\n" # "dpi f = {}\n" # "w inch = {}\n" # "h inch = {}\n" # "dpi fs = {}\n" # "dpi log x = {}\n" # "dpi log y = {}".format(w, h, dpival, dpivalf, winch, hinch, dpivalfs, dpilogx, dpilogy)) pass def run(self): """ Setup database and basic configuration """ # basic settings must be done is_set = check_settings(self._settings.settings) if is_set: try: _ = self._employees.employee["fullname"] except KeyError: msgbox = QMessageBox() msgbox.about( self, __appname__, "Der er en fejl i dine indstillinger.\nKontroller dem venligst.\nTak." ) else: msgbox = QMessageBox() msgbox.about( self, __appname__, "App'en skal bruge nogle oplysninger.\nRing kontoret hvis du er i tvivl.\nTak." ) self.show_page_settings() # if requested check server data try: if utils.int2bool(self._settings.settings["sc"]): # update sync status status = utils.refresh_sync_status(self._settings) self._settings.settings["sac"] = status[0][1].split()[0] self._settings.settings["sap"] = status[1][1].split()[0] self._settings.update() except KeyError: pass # display known sync data self.display_sync_status() def set_indexes(self): """ Save page index to settings :return: """ try: self._settings.settings["cust_idx"] = self._customers.customer[ "customer_id"] except KeyError: self._settings.settings["cust_idx"] = 0 try: _ = self._settings.settings["page_idx"] except KeyError: self._settings.settings[ "page_idx"] = self.widgetAppPages.currentIndex() self._settings.update() def set_input_enabled(self, arg: bool) -> None: """Enable inputs""" self.checkVisitSas.setEnabled(arg) self.comboOrderItem.setEnabled(arg) self.comboOrderSku.setEnabled(arg) self.textVisitPcs.setEnabled(arg) self.textVisitLinePrice.setEnabled(arg) self.textVisitLineDiscount.setEnabled(arg) @pyqtSlot(name="app_exit") def app_exit(self): """ Exit - save current customer """ # customer id try: self._settings.settings["cust_idx"] = self._customers.customer[ "customer_id"] except KeyError: self._settings.settings["cust_idx"] = 0 self._settings.update() app.quit() @pyqtSlot(name="archive_contacts") def archive_contacts(self): """ Save changes made to contacts """ # TODO save changes made to contacts msgbox = QMessageBox() msgbox.information(self, __appname__, "# TODO save changes made to contacts", QMessageBox.Ok) @pyqtSlot(name="archive_customer") def archive_customer(self): """ Slot for updateCustomer triggered signal """ if not self._customers.customer: # msgbox triggered if no current is selected msgbox = QMessageBox() msgbox.information(self, __appname__, "Det kan jeg ikke på nuværende tidspunkt!", QMessageBox.Ok) return False # assign input field values to current object self._customers.customer["company"] = self.textCompany.text() self._customers.customer["address1"] = self.textAddress1.text() self._customers.customer["address2"] = self.textAddress2.text() self._customers.customer["zipcode"] = self.textZipCode.text() self._customers.customer["city"] = self.textCityName.text() self._customers.customer["phone1"] = self.textPhone1.text() self._customers.customer["phone2"] = self.textPhone2.text() self._customers.customer["email"] = self.textEmail.text() self._customers.customer["factor"] = self.textFactor.text() self._customers.customer[ "infotext"] = self.textArchivedVisitNote.toPlainText() self._customers.customer["modified"] = 1 self._customers.update() @pyqtSlot(name="archive_settings") def archive_settings(self): """ Archive settings :return: """ checkok = True items = [] if self.textAppUserMail.text() == "": items.append("Gruppe 'Sælger' -> 'Email'") checkok = False if self.textAppUserPass.text() == "": items.append("Gruppe 'Sælger' -> 'Adgangsfrase'") checkok = False if self.textAppUserCountry.text() == "": items.append("Gruppe: Sælger -> landekode") checkok = False if self.textAppMailServer.text() == "": items.append("Gruppe: Intern -> Mailserver") checkok = False if self.textAppMailServerPort.text() == "": items.append("Gruppe: Intern -> Port") checkok = False if self.textAppMailOrderTo == "": items.append("Gruppe: Intern -> Mail til") checkok = False if self.textAppDataServer == "": items.append("Gruppe: Intern -> Dataserver") checkok = False # inform user about settings validity msgbox = QMessageBox() if not checkok: msgbox.warning( self, __appname__, "Der er mangler i dine indstillinger!\n{}".format( "\n".join(items)), QMessageBox.Ok) return False # update password in settings if len(self.textAppUserPass.text()) < 97: self._settings.settings["userpass"] = passwdFn.hash_password( self.textAppUserPass.text()) if len(self.textExtMailServerPass.text()) < 97: self._settings.settings["mailpass"] = passwdFn.hash_password( self.textExtMailServerPass.text()) self._settings.settings["usermail"] = self.textAppUserMail.text( ).lower() self._settings.settings["usercountry"] = self.textAppUserCountry.text() self._settings.settings["http"] = self.textAppDataServer.text() self._settings.settings["smtp"] = self.textAppMailServer.text() self._settings.settings["port"] = self.textAppMailServerPort.text() self._settings.settings["mailto"] = self.textAppMailOrderTo.text() self._settings.settings["sc"] = utils.bool2int( self.checkServerData.isChecked()) self._settings.settings["mailserver"] = self.textExtMailServer.text( ).lower() self._settings.settings["mailport"] = self.textExtMailServerPort.text() self._settings.settings["mailuser"] = self.textExtMailServerUser.text() self._settings.update() self._employees.load(self._settings.settings["usermail"]) msgbox.information(self, __appname__, "Indstillinger opdateret.", QMessageBox.Ok) @pyqtSlot(name="archive_visit") def archive_visit(self): """ Slot for saving the visit """ self.toolButtonCustomerVisit.setEnabled(False) # save visit head contents self._visits.visit["po_buyer"] = self.textVisitBuyer.text() self._visits.visit["po_number"] = self.textVisitPoNumber.text() self._visits.visit["po_company"] = self.textVisitDelCompany.text() self._visits.visit["po_address1"] = self.textVisitDelAddress1.text() self._visits.visit["po_address2"] = self.textVisitDelAddress2.text() self._visits.visit["po_postcode"] = self.textVisitDelZip.text() self._visits.visit["po_postofffice"] = self.textVisitDelCity.text() self._visits.visit["po_country"] = self._employees.employee["country"] self._visits.visit["po_note"] = self.textVisitOrderNote.text() self._visits.visit["prod_demo"] = self.textVisitProductDemo.text() self._visits.visit["prod_sale"] = self.textVisitProductSale.text() self._visits.visit["po_sas"] = self.textVisitSas.text() self._visits.visit["po_sale"] = self.textVisitSale.text() self._visits.visit["po_total"] = self.textVisitTotal.text() self._visits.visit["visit_note"] = self.textVisitInfo.toPlainText() # TODO: save visitdetails @pyqtSlot(name="create_contact") def create_contact(self): """ Save changes made to contacts """ # TODO add new contact msgbox = QMessageBox() msgbox.information(self, __appname__, "# TODO add new contact", QMessageBox.Ok) @pyqtSlot(name="create_customer") def create_customer(self): """ Slot for createCustomer triggered signal """ if not self.textNewCompany.text() or not self.textNewPhone1.text(): msgbox = QMessageBox() msgbox.information( self, __appname__, "Snap - Jeg mangler:\n Firma navn \n Telefon nummer", QMessageBox.Ok) else: msgbox = QMessageBox() msgbox.information( self, __appname__, "Gem kunde til database\n\n" + self.textNewCompany.text() + "\n" + self.textNewPhone1.text(), QMessageBox.Ok) @pyqtSlot(name="create_report") def create_report(self): """ Slot for Report triggered signal """ try: # check the report date # no report triggers KeyError which in turn launches the CreateReportDialog repdate = self._reports.report["rep_date"] if not repdate == self.textWorkdate.text(): # if active report is not the same replace it with workdate self._reports.load(self.textWorkdate.text()) # trigger a KeyError if no report is current which launches the CreateReportDialog repdate = self._reports.report["rep_date"] # check if the report is sent if self._reports.report["sent"] == 1: # we do not allow visits to be created on a report which is closed self.buttonCreateVisit.setEnabled(False) else: self.buttonCreateVisit.setEnabled(True) infotext = "Rapport aktiv for: {}".format(repdate) msgbox = QMessageBox() msgbox.information(self, __appname__, infotext, QMessageBox.Ok) return True except KeyError: # Show report dialog create_report_dialog = ReportDialogCreate(self.textWorkdate.text()) if create_report_dialog.exec_(): # user chosed to create a report self.textWorkdate.setText(create_report_dialog.workdate) # try load a report for that date self._reports.load(self.textWorkdate.text()) try: # did the user choose an existing report _ = self._reports.report["rep_date"] infotext = "Eksisterende rapport hentet: {}".format( self.textWorkdate.text()) except KeyError: # create the report self._reports.create(self._employees.employee, self.textWorkdate.text()) infotext = "Rapport Createtet for: {}".format( self.textWorkdate.text()) msgbox = QMessageBox() msgbox.information(self, __appname__, infotext, QMessageBox.Ok) return True else: msgbox = QMessageBox() msgbox.information( self, __appname__, "Den aktive rapport er <strong>IKKE</strong> ændret!", QMessageBox.Ok) return False @pyqtSlot(name="load_visit") def load_visit(self): """ Slot for loading the visit dialog """ try: # do we have a report _ = self._reports.report["rep_date"] active_report = True except KeyError: active_report = self.create_report() if active_report: self._reports.load(workdate=self.textWorkdate.text()) try: # do we have a customer _ = self._customers.customer["company"] except KeyError: msgbox = QMessageBox() msgbox.information( self, __appname__, "Ingen valgt kunde! Besøg kan ikke oprettes.", QMessageBox.Ok) return if self.textCustId.text() is not "" and \ self.textCustId.text() is not self._customers.customer["customer_id"]: confirm = QMessageBox() val = confirm.question( self, __appname__, f"Du har et uafsluttet besøg på {self.textVisitCompany.text()}." f"<br/>Vil du slette det?", confirm.Yes | confirm.No) if val == confirm.No: self._customers.lookup_by_id(self.textCustId.text()) else: self._archivedVisits.delete(self.textVisitId.text()) self.toolButtonCustomerVisit.setEnabled(True) # self.widgetAppPages.setCurrentIndex(PAGE_VISIT) customer_pricelist = self._products workdate = self.textWorkdate.text() customerid = self._customers.customer["customer_id"] reportid = self._reports.report["report_id"] employeeid = self._employees.employee["employee_id"] self.textCustId.setText(str(customerid)) self.textVisitDate.setText(self.textWorkdate.text()) self.textVisitCompany.setText(self._customers.customer["company"]) try: """ load visits for workdate """ self._visits.list_by_date(workdate) self.textVisitId.setText(str(self._visits.visit["visit_id"])) except (KeyError, ): self.textVisitId.setText( str( self._visits.add(reportid, employeeid, customerid, workdate))) self._visits.visit["visit_type"] = "R" if self._customers.customer["account"] == "NY": self._visits.visit["visit_type"] = "N" visit_id = self.textVisitId.text() self._orderLines = OrderLine() self._orderLines.load_visit(visit_id) self.widgetTableSale.setColumnWidth(0, 43) # line_type D/N/S self.widgetTableSale.setColumnWidth(1, 44) # pcs self.widgetTableSale.setColumnWidth(2, 44) # item self.widgetTableSale.setColumnWidth(3, 123) # sku self.widgetTableSale.setColumnWidth(4, 153) # text self.widgetTableSale.setColumnWidth(5, 60) # price self.widgetTableSale.setColumnWidth(6, 50) # discount self.widgetTableSale.setColumnWidth(6, 60) # amount self.widgetTableSale.setColumnWidth(7, 30) # SAS lines = self._orderLines.list_ visit_sale = 0.0 visit_sas = 0.0 visit_total = 0.0 line_demo = 0 line_sale = 0 row_number = 0 self.widgetTableSale.setRowCount(len(lines) - 1) for line in lines: # "line_id", "visit_id", # "pcs", "sku", "text", "price", "sas", "discount", # "linetype", "linenote", "item" amount = float( line["pcs"]) * line["price"] * line["discount"] / 100 if line["sas"] == 1: visit_sas += amount else: visit_sale += amount visit_total += amount # self.widgetTableSale.setRowHeight(row_number, 12) if line["linetype"].lower() == "d": line_demo += 1 row_number = line_demo self.widgetTableDemo.setRowCount(row_number) c1 = QTableWidgetItem() c1.setText(line["linetype"]) self.widgetTableDemo.setItem(row_number, 0, c1) c2 = QTableWidgetItem() c2.setText(str(line["pcs"])) self.widgetTableDemo.setItem(row_number, 1, c2) c3 = QTableWidgetItem() c3.setText(str(line["item"])) self.widgetTableDemo.setItem(row_number, 2, c3) else: line_sale += 1 row_number = line_sale self.widgetTableSale.setRowCount(row_number) c1 = QTableWidgetItem() c1.setText(line["linetype"]) self.widgetTableSale.setItem(row_number, 0, c1) c2 = QTableWidgetItem() c2.setText(str(line["pcs"])) self.widgetTableSale.setItem(row_number, 1, c2) c3 = QTableWidgetItem() c3.setText(str(line["item"])) self.widgetTableSale.setItem(row_number, 2, c3) c4 = QTableWidgetItem() c4.setText(line["sku"]) self.widgetTableSale.setItem(row_number, 3, c4) c5 = QTableWidgetItem() c5.setText(line["text"]) self.widgetTableSale.setItem(row_number, 4, c5) c6 = QTableWidgetItem() c6.setText(str(line["price"])) self.widgetTableSale.setItem(row_number, 5, c6) c7 = QTableWidgetItem() c6.setText(str(line["discount"])) self.widgetTableSale.setItem(row_number, 6, c7) c9 = QTableWidgetItem() c9.setText(utils.int2strdk(line["sas"])) self.widgetTableSale.setItem(row_number, 7, c9) c10 = QTableWidgetItem() c10.setText(line["linenote"]) self.widgetTableSale.setItem(row_number, 8, c10) # Setup pricelist and selection combos factor = self._customers.customer["factor"] if not factor: factor = 0.0 for item in customer_pricelist.products: if factor is not 0.0: item["price"] = item["price"] * factor item["d2"] = item["d2"] * factor item["d3"] = item["d3"] * factor item["d4"] = item["d4"] * factor item["d6"] = item["d6"] * factor item["d8"] = item["d8"] * factor item["d12"] = item["d12"] * factor item["d24"] = item["d24"] * factor item["d48"] = item["d48"] * factor item["d96"] = item["d96"] * factor item["min"] = item["min"] * factor item["net"] = item["net"] * factor self.comboLineItem.addItem(item["item"], [item["sku"], item["name1"], item]) self.comboLineSku.addItem(item["sku"], [item["item"], item["name1"], item]) # connect to signals self.buttonArchiveVisit.clicked.connect(self.archive_visit) self.comboOrderItem.currentIndexChanged.connect( self.on_order_item_changed) self.comboOrderSku.currentIndexChanged.connect( self.on_order_sku_changed) self.comboOrderSku.editTextChanged.connect(self.on_order_sku_changed) @pyqtSlot(name="data_export") def data_export(self): """ Export Database backup file """ # TODO: Create CSV data backup msgbox = QMessageBox() msgbox.information(self, __appname__, "TODO: Create Database Backup File", QMessageBox.Ok) @pyqtSlot(name="data_import") def data_import(self): """ Import Database backup file """ # TODO: Restore from CSV data backup msgbox = QMessageBox() msgbox.information(self, __appname__, "TODO: Import Database Backup File", QMessageBox.Ok) @pyqtSlot(name="get_customers") def get_customers(self): """ Slot for getCustomers triggered signal """ import_customers = GetCustomersDialog(app, customers=self._customers, employees=self._employees, settings=self._settings) import_customers.sig_done.connect(self.on_get_customers_done) import_customers.exec_() @pyqtSlot(name="get_pricelist") def get_pricelist(self): """ Slot for getProducts triggered signal """ import_product = GetPricelistDialog(app, products=self._products, settings=self._settings) import_product.sig_done.connect(self.on_get_products_done) import_product.exec_() @pyqtSlot(name="on_add_demo") def on_add_demo(self): """ Add line to product demo table :return: """ row_count = self.widgetTableDemo.rowCount() self.widgetTableDemo.setRowCount(row_count + 1) @pyqtSlot(name="on_add_sale") def on_add_sale(self): """ Add line to product sale table :return: """ row_count = self.widgetTableSale.rowCount() self.widgetTableSale.setRowCount(row_count + 1) @pyqtSlot(name="on_remove_demo") def on_remove_demo(self): """ Remove line from product demo table :return: """ if self.widgetTableDemo.currentIndex(): self.widgetTableDemo.removeRow(self.widgetTableDemo.currentIndex()) @pyqtSlot(name="on_remove_sale") def on_remove_sale(self): """ Remove line from product sale table :return: """ if self.widgetTableSale.currentIndex(): self.widgetTableSale.removeRow(self.widgetTableSale.currentIndex()) @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem, name="on_customer_changed") def on_customer_changed(self, current, previous): """ Slot for treewidget current item changed signal Used to respond to changes in the customer list and update the related customer info Args: current: currently selected item previous: previous selected item """ try: # account = current.text(0) phone = current.text(2) company = current.text(4) # load customer self._customers.lookup(phone, company) # fill out fields self.textAccount.setText(self._customers.customer["account"]) self.textCompany.setText(self._customers.customer["company"]) self.textAddress1.setText(self._customers.customer["address1"]) self.textAddress2.setText(self._customers.customer["address2"]) self.textZipCode.setText(self._customers.customer["zipcode"]) self.textCityName.setText(self._customers.customer["city"]) self.textPhone1.setText(self._customers.customer["phone1"]) self.textPhone2.setText(self._customers.customer["phone2"]) self.textEmail.setText(self._customers.customer["email"]) self.textFactor.setText(str(self._customers.customer["factor"])) self.textCustomerNotes.setText( self._customers.customer["infotext"]) self.textCustomerNameCreateVisit.setText( self._customers.customer["company"]) except AttributeError: pass except KeyError: pass # load customer infos self.populate_contact_list() self.populate_archived_visits() self.populate_archived_visit_details() self.load_visit() @pyqtSlot(name="on_csv_import_done") def on_csv_import_done(self): """ Slog for csv import done signal """ self.populate_customer_list() @pyqtSlot(QTreeWidgetItem, name="on_customer_clicked") def on_customer_double_clicked(self, current): """ Customer selected in :param current: :return: """ print("current: {}".format(current)) self.toolButtonCustomer.click() @pyqtSlot(name="on_get_customers_done") def on_get_customers_done(self): """ Slot for getCustomers finished signal """ self.populate_customer_list() lsc = datetime.date.today().isoformat() self.textCustomerLocalDate.setText(lsc) self._settings.settings["lsc"] = lsc self._settings.update() @pyqtSlot(name="on_get_products_done") def on_get_products_done(self): """ Slot for getProducts finished signal """ _ = self._products.products lsp = datetime.date.today().isoformat() self.textPricelistLocalDate.setText(lsp) self._settings.settings["lsp"] = lsp self._settings.update() @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem, name="on_visit_changed") def on_visit_changed(self, current, previous): """ Response to current visit changed Args: current: previous: """ try: self._archivedVisits.load_visit(current.text(0)) except AttributeError: pass except KeyError: pass self.populate_archived_visit_details() @pyqtSlot(name="on_order_item_changed") def on_order_item_changed(self): """Update SKU combo when item changes""" self.comboOrderSku.setCurrentText( self.comboOrderItem.itemData( self.comboOrderItem.currentIndex())[0]) self.update_orderline_text( self.comboOrderItem.itemData( self.comboOrderItem.currentIndex())[1]) utils.item_price( self.comboOrderItem.itemData( self.comboOrderItem.currentIndex())[3], self.textVisitPcs.text()) @pyqtSlot(name="on_order_sku_changed") def on_order_sku_changed(self): """Update ITEM combo when sku changes""" self.comboOrderItem.setCurrentText( self.comboOrderSku.itemData(self.comboOrderSku.currentIndex())[0]) self.update_orderline_text( self.comboOrderSku.itemData(self.comboOrderSku.currentIndex())[1]) utils.item_price( self.comboOrderSku.itemData(self.comboOrderSku.currentIndex()[3]), self.textVisitPcs.text()) def update_orderline_text(self, text): self.textVisitLineText.setText(text) @pyqtSlot(name="show_csv_import_dialog") def show_csv_import_dialog(self): """ Slot for fileImport triggered signal """ if self._customers.customers: msgbox = QMessageBox() msgbox.warning( self, __appname__, "<strong>Ved import slettes alle eksisterende data</strong>!<br/><br/>" "Det er alt eller intet af hensyn til datas sammenhæng.<br/>" "Du <strong>SKAL</strong> importere <strong>ALLE<strong> tabeller fra listen!<br/><br/>" "<strong>Gør du ikke det giver det uløselige problemer</strong>!", QMessageBox.Ok) # app, contact, customer, detail, employee, report, visit, tables import_dialog = CsvFileImportDialog( app, contacts=self._contacts, customers=self._customers, employees=self._employees, orderlines=self._archivedOrderlines, reports=self._reports, tables=config.CSV_TABLES, visits=self._archivedVisits) import_dialog.sig_done.connect(self.on_csv_import_done) import_dialog.exec_() @pyqtSlot(name="show_page_customer") def show_page_customer(self): """ Show page with customer """ self.set_indexes() self.widgetAppPages.setCurrentIndex(PAGE_CUSTOMER) @pyqtSlot(name="show_page_customer_visits") def show_page_customer_visits(self): """ Show page with customer visits and orders """ self.set_indexes() self.widgetAppPages.setCurrentIndex(PAGE_CUSTOMER_VISITS) @pyqtSlot(name="show_page_customers") def show_page_customers(self): """ Show page with customer list """ self.set_indexes() self.widgetAppPages.setCurrentIndex(PAGE_CUSTOMERS) @pyqtSlot(name="show_page_info") def show_page_info(self): """ Show page with about Qt and Eordre """ self.set_indexes() self.widgetAppPages.setCurrentIndex(PAGE_INFO) @pyqtSlot(name="show_page_pricelist") def show_page_pricelist(self): """ Show page with pricelist """ self.set_indexes() self.widgetAppPages.setCurrentIndex(PAGE_PRICELIST) @pyqtSlot(name="show_page_report") def show_page_report(self): """ Slot for masterData triggered signal """ self.set_indexes() self.widgetAppPages.setCurrentIndex(PAGE_REPORT) @pyqtSlot(name="show_page_reports") def show_page_reports(self): """ Show page with a report list """ self.set_indexes() self.widgetAppPages.setCurrentIndex(PAGE_REPORTS) @pyqtSlot(name="show_page_settings") def show_page_settings(self): """ Show page with settings """ self.set_indexes() self.populate_settings_page() self.widgetAppPages.setCurrentIndex(PAGE_SETTINGS) @pyqtSlot(name="show_page_visit") def show_page_visit(self): """ Show page with visit """ self.set_indexes() self.load_visit() self.widgetAppPages.setCurrentIndex(PAGE_VISIT) @pyqtSlot(name="zero_database") def zero_database(self): """ Slot for zeroDatabase triggered signal """ confirm = QMessageBox() val = confirm.question(self, __appname__, "Alle salgsdata slettes<br/>Vil du fortsætte?", confirm.Yes | confirm.No) if val == confirm.Yes: self._contacts.recreate_table() self._customers.recreate_table() self._archivedOrderlines.recreate_table() self._archivedVisits.recreate_table() self._reports.recreate_table() self.populate_contact_list() self.populate_archived_visit_details() self.populate_archived_visits() self.populate_customer_list() self._settings.settings["lsc"] = "" self._settings.settings["sac"] = "" self._settings.settings["lsp"] = "" self._settings.settings["sap"] = "" self._settings.update() self.display_sync_status() msgbox = QMessageBox() msgbox.information(self, __appname__, "Salgsdata er nulstillet!", QMessageBox.Ok)
def setUp(self): self.storage_service = StorageService() self.pattern_name = 'test_pattern' self.pattern = self.create_pattern(self.pattern_name) self.settings_name = 'test_settings' self.settings = Settings(self.settings_name)
def get(self): images_total = 0 images_backed_up = 0 try: self.response.headers['Content-Type'] = 'text/plain' settings = Settings.get() if not settings.dropbox_access_token: self.log('No access token available, no backup will be performed.') return posts = [p for p in Post.query().order(Post.date).fetch()] self.log('Backing up %s posts to Dropbox' % len(posts)) post_text = StringIO() for p in posts: post_text.write(p.date.strftime('%Y-%m-%d')) post_text.write('\r\n\r\n') post_text.write(p.text.replace('\r\n', '\n').replace('\n', '\r\n').rstrip()) post_text.write('\r\n\r\n') result = self.put_file(settings.dropbox_access_token, 'MyLife.txt', post_text.getvalue().encode('utf-8')) post_text.close() self.log('Backed up posts. Revision: %s' % result['rev']) self.log('Fetching Dropbox file list') files_in_dropbox = self.get_dropbox_filelist(settings.dropbox_access_token) self.log('Got %s files from Dropbox' % len(files_in_dropbox)) self.log('Fetching images...') images = [i for i in UserImage.query().order(UserImage.date).fetch()] self.log('Total images in MyLife: %s' % len(images)) not_backed_up = [i for i in images if not i.backed_up_in_dropbox] not_in_dropbox = [i for i in images if not i.filename in files_in_dropbox] self.log('\nFiles not backed up: \n\n' + '\n'.join([i.filename for i in not_backed_up])) self.log('\nFiles marked as backed up, but not in Dropbox: \n\n' + '\n'.join([i.filename for i in not_in_dropbox])) images = not_backed_up + not_in_dropbox images_total = len(images) self.log('Found %s images that need to be backed up in Dropbox' % images_total) for img in images: self.log('Backing up %s' % img.filename) bytes = filestore.read(img.original_size_key) result = self.put_file(settings.dropbox_access_token, img.filename, bytes) self.log('Backed up %s. Revision: %s' % (img.filename, result['rev'])) img.backed_up_in_dropbox = True img.put() images_backed_up += 1 settings.dropbox_last_backup = datetime.datetime.now() settings.put() self.log('Finished backup successfully') except apiproxy_errors.OverQuotaError, ex: self.log(ex) log_error('Error backing up to Dropbox, quota exceeded', 'The backup operation did not complete because it ran out of quota. ' + 'The next time it runs it will continue backing up your posts and images.' + '%s images out of %s were backed up before failing' % (images_backed_up, images_total))
def save_setting(key, value): Settings().update(key=key, value=value)
def get_settings(self) -> Settings: data = self.__settings.get(doc_id=0) return Settings(data['frame_buffer_size'], data['video_path'])
def get(self, year, month): Settings.get() #Force email address update... now = datetime.datetime.now() if not year: last_post = Post.query().order(-Post.date).get() if last_post: year, month = last_post.date.year, last_post.date.month else: year, month = now.year, now.month else: year, month = int(year), int(month) from_date = datetime.date(year, month, 1) to_month = month + 1 to_year = year if to_month == 13: to_month = 1 to_year += 1 to_date = datetime.date(to_year, to_month, 1) posts = [ p for p in Post.query( ndb.AND(Post.date >= from_date, Post.date < to_date)).order( -Post.date).fetch() ] month_name = from_date.strftime('%B %Y') #Get month list months = PostCounter.get().months[:] def cmp_months(a, b): if a.year != b.year: return cmp(a.year, b.year) else: return cmp(a.month, b.month) months.sort(cmp_months) archive = [] next_link, prev_link = None, None for i, m in enumerate(months): date = datetime.date(m.year, m.month, 1) descr = '%s, %s posts' % (date.strftime('%B %Y'), m.count) value = date.strftime('%Y-%m') archive.append((value, descr, m.year == year and m.month == month)) if m.year == year and m.month == month: if i != 0: prev_link = '/past/%s' % datetime.date( months[i - 1].year, months[i - 1].month, 1).strftime('%Y-%m') if i < len(months) - 1: next_link = '/past/%s' % datetime.date( months[i + 1].year, months[i + 1].month, 1).strftime('%Y-%m') if not archive: archive.append(('', '%s, 0 posts' % now.strftime('%B %Y'), False)) data = { "page": "past", "posts": posts, "month": month_name, "archive": archive, "next": next_link, "prev": prev_link } self.response.write(get_template('past.html').render(data))
class Library(): def __init__(self): self.films = [] self.settings = Settings() global portable_paths portable_paths = self.settings.portable_paths self._load() def _load(self): content = json.read(_filename) if content: for dictionary in content: self.films.append(Film(**dictionary)) def save(self): json.write(self.films, _filename) self.settings.save() def update(self): from os import walk found = [] for directory in self.settings.directories(): if path.exists(directory): for root, dirnames, filenames in walk(directory): for filename in filenames: # Get around the 256 character limit by prepending \\\\?\\ absolute = path.join("\\\?\\", root, filename) if filename.lower().endswith(tuple(self.settings.file_extensions)): mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime = stat(absolute) if size > self.settings.minimum_size_byte: found.append(Film(abspath=absolute, last_modification=get_datetime(mtime), size=size)) else: self.settings.directories().remove(directory) # Film has been removed from disk - remove from library for film in self.films: if film not in found: #print('Removed: {}'.format(film.print())) self.films.remove(film) #del film # New film - add to library for film in found: if film not in self.films: #print('Found: {}'.format(film.print())) self.films.append(film) def random_movie(self): import random if self.films: found = [] for film in self.films: if not film.watched: found.append(film) if found: return random.choice(found) else: print("There are no unwatched films.") return None else: print("There are no films added.") return None
def __init__(self): self.films = [] self.settings = Settings() global portable_paths portable_paths = self.settings.portable_paths self._load()
from common.database.databasetype import DatabaseType from models.settings import Settings, Logging import sys if __name__ == '__main__': settings = Settings.singleton() if settings.service_already_running(): Logging.error("Service teleinfo is already running") sys.exit() open(settings.pidfile, 'w').write(settings.pid) try: Logging.info("Start Teleinfo application") settings.init_db(DatabaseType.SQLITE) # Creation of models import models from serial_teleinfo import teleinfo Logging.info("Check creation of tables") settings.database.init_tables() teleinfo.Teleinfo().run() finally: settings.dispose()
def send(self, is_intro_email=False): try: now = datetime.datetime.now() settings = Settings.get() if is_intro_email: current_time = now logging.info('Sending intro email to %s' % settings.email_address) else: current_time, id, name, offset = self.get_time_in_timezone(settings) if current_time.hour != settings.email_hour: logging.info('Current time for %s is %s, not sending email now, will send at %02d:00' % (name, current_time, settings.email_hour)) return today = current_time.date() if self.check_if_intro_email_sent_today(today): logging.info('Already sent the intro email today, skipping this email for now') return self.check_if_mail_already_sent(today) slug_id = self.get_slug() slug = Slug(slug=slug_id, date=today) slug.put() subject = "It's %s, %s %s - How did your day go?" % (today.strftime('%A'), today.strftime("%b"), today.day) app_id = app_identity.get_application_id() sender = "MyLife <post+%s@%s.appspotmail.com>" % (slug.slug, app_id) message = mail.EmailMessage(sender=sender, subject=subject) message.to = settings.email_address if not settings.email_address: log_error('Missing To in daily email', 'There is no configured email address in your settings. Please visit your settings page to configure it so we can send you your daily email.') return message.body = """ Just reply to this email with your entry. OLD_POST You can see your past entries here: https://APP_ID.appspot.com/past """.replace('APP_ID', app_id) message.html = """ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.= w3.org/TR/html4/loose.dtd"> <html> <head> <title></title> </head> <body> Just reply to this email with your entry. <br> <br> OLD_POST <a href="https://APP_ID.appspot.com/past">Past entries</a> </body> </html> """.replace('APP_ID', app_id) if is_intro_email: intro_msg = "Welcome to MyLife. We've sent you this email immediately so you can try the system out. In the future we will email you once a day and include an old post in each email. You can configure when you get your email and which email address we should use on the settings page." message.html = message.html.replace('OLD_POST', intro_msg + '<br><br>') message.body = message.body.replace('OLD_POST', intro_msg + '\r\n\r\n') else: #Lets try to put in an old post... old_post, old_type = self.get_old_post(today) if old_post and settings.include_old_post_in_entry: old_post_text = 'Remember this? One %s ago you wrote:\r\n\r\n' % old_type old_post_text += old_post.text.rstrip() + '\r\n\r\n' message.body = re.sub(r'OLD_POST\r?\n', old_post_text, message.body) old_post_text = re.sub(r'\r?\n', '<br>', old_post_text) message.html = re.sub(r'OLD_POST\r?\n', old_post_text, message.html) else: message.body = re.sub('OLD_POST\r?\n', '', message.body) message.html = re.sub('OLD_POST\r?\n', '', message.html) message.send() if is_intro_email: logging.info('Sent intro email') else: if old_post: logging.info('Sent daily email to %s, using old post from %s' % (message.to, old_post.date)) else: logging.info('Sent daily email to %s, could not find old post' % message.to) return 'Email sent' except: log_error('Failed to send daily email', traceback.format_exc(6)) return 'Failed sending email: %s' % traceback.format_exc(6)
def send(self, is_intro_email=False, force_send=False, date=None): try: now = datetime.datetime.now() settings = Settings.get() if is_intro_email: current_time = now logging.info('Sending intro email to %s' % settings.email_address) else: current_time, id, name, offset = self.get_time_in_timezone(settings) if current_time.hour != settings.email_hour and not force_send: logging.info('Current time for %s is %s, not sending email now, will send at %02d:00' % (name, current_time, settings.email_hour)) return if date and force_send: today = date #Allow overriding this stuff else: today = current_time.date() if self.check_if_intro_email_sent_today(today) and not force_send: logging.info('Already sent the intro email today, skipping this email for now') return #Check if we've already sent an email slug = Slug.query(Slug.date == today).get() if slug and not force_send: msg = 'Tried to send another email on %s, already sent %s' % (date, slug.slug) log_error('Tried to send email again', msg) raise Exception(msg) if not slug: slug_id = self.get_slug() slug = Slug(slug=slug_id, date=today) slug.put() subject = "It's %s, %s %s - How did your day go?" % (today.strftime('%A'), today.strftime("%b"), today.day) app_id = app_identity.get_application_id() sender = "MyLife <post+%s@%s.appspotmail.com>" % (slug.slug, app_id) message = mail.EmailMessage(sender=sender, subject=subject) message.to = settings.email_address if not settings.email_address: log_error('Missing To in daily email', 'There is no configured email address in your settings. Please visit your settings page to configure it so we can send you your daily email.') return message.body = """ Just reply to this email with your entry. OLD_POST """.replace('APP_ID', app_id) message.html = """ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.= w3.org/TR/html4/loose.dtd"> <html> <head> <title></title> </head> <body> Just reply to this email with your entry. <br> <br> OLD_POST </body> </html> """.replace('APP_ID', app_id) if is_intro_email: intro_msg = "Welcome to MyLife. We've sent you this email immediately so you can try the system out. In the future we will email you once a day and include an old post in each email. You can configure when you get your email and which email address we should use on the settings page." message.html = message.html.replace('OLD_POST', intro_msg + '<br><br>') message.body = message.body.replace('OLD_POST', intro_msg + '\r\n\r\n') else: #Lets try to put in an old post... old_post, old_type = self.get_old_post(today) if old_post and settings.include_old_post_in_entry: logging.info('Going to use old post %s because %s' % (old_post, settings.include_old_post_in_entry)) if (old_type == 'random'): old_post_text = 'Remember this? On <b>%s, %s %s, %s</b> you wrote:\r\n\r\n' % (old_post.date.strftime('%A'), old_post.date.strftime("%b"), old_post.date.day, old_post.date.strftime("%Y")) else: old_post_text = 'Remember this? One %s ago you wrote:\r\n\r\n' % old_type old_post_text += old_post.text.rstrip() + '\r\n\r\n' message.body = re.sub(r'OLD_POST\r?\n', old_post_text, message.body) old_post_text = re.sub(r'\r?\n', '<br>', old_post_text) message.html = re.sub(r'OLD_POST\r?\n', old_post_text, message.html) else: logging.info('Not using Old post %s because %s' % (old_post, settings.include_old_post_in_entry)) message.body = re.sub('OLD_POST\r?\n', '', message.body) message.html = re.sub('OLD_POST\r?\n', '', message.html) message.send() if is_intro_email: logging.info('Sent intro email') else: if old_post: logging.info('Sent daily email to %s, using old post from %s' % (message.to, old_post.date)) else: logging.info('Sent daily email to %s, could not find old post' % message.to) return 'Email sent' except: log_error('Failed to send daily email', traceback.format_exc(6)) return 'Failed sending email: %s' % traceback.format_exc(6)
def get(self, values): settings = Settings.get_instance() for category in settings: values[category] = sorted(settings[category].values.items()) self.response.write(render("templates/configuracoes.html", values))
def __init__(self, parent=None): """ Initialize MainWindow class """ super(MainWindow, self).__init__(parent) self.setupUi(self) thread = QThread() thread.currentThread().setObjectName(__appname__) configfn.check_config_folder() # Check appdata folder in users home self.textWorkdate.setText(datetime.date.today().isoformat() ) # initialize workdate to current date self._archivedOrderlines = OrderLine() # Initialize Detail object self._archivedVisits = Visit() # Initialize Visit object self._contacts = Contact() # Initialize Contact object self._customers = Customer() # Initialize Customer object self._employees = Employee() # Initialize Employee object self._orderLines = OrderLine() self._products = Product() # Initialize Product object self._reports = Report() # Initialize Report object self._settings = Settings() # Initialize Settings object self._visits = Visit() self.buttonArchiveContacts.clicked.connect(self.archive_contacts) self.buttonArchiveCustomer.clicked.connect(self.archive_customer) self.buttonArchiveVisit.clicked.connect(self.archive_visit) self.buttonCreateContact.clicked.connect(self.create_contact) self.buttonCreateCustomer.clicked.connect(self.create_customer) self.buttonCreateReport.clicked.connect(self.create_report) self.buttonCreateVisit.clicked.connect(self.load_visit) self.buttonGetCustomers.clicked.connect(self.get_customers) self.buttonGetPricelist.clicked.connect(self.get_pricelist) self.toolButtonArchiveSettings.clicked.connect(self.archive_settings) self.toolButtonCustomer.clicked.connect(self.show_page_customer) self.toolButtonCustomers.clicked.connect(self.show_page_customers) self.toolButtonCustomerVisits.clicked.connect( self.show_page_customer_visits) self.toolButtonExit.clicked.connect(self.app_exit) self.toolButtonInfo.clicked.connect(self.show_page_info) self.toolButtonPricelist.clicked.connect(self.show_page_pricelist) self.toolButtonReport.clicked.connect(self.show_page_report) self.toolButtonReports.clicked.connect(self.show_page_reports) self.toolButtonSettings.clicked.connect(self.show_page_settings) self.toolButtonCustomerVisit.clicked.connect(self.show_page_visit) self.toolButtonDeleteSalesData.clicked.connect(self.zero_database) self.toolButtonExportDatabase.clicked.connect(self.data_export) self.toolButtonImportCsvData.clicked.connect( self.show_csv_import_dialog) self.toolButtonImportDatabase.clicked.connect(self.data_import) self.widgetCustomers.currentItemChanged.connect( self.on_customer_changed) self.widgetCustomers.itemDoubleClicked.connect( self.on_customer_double_clicked) self.widgetArchivedVisits.currentItemChanged.connect( self.on_visit_changed) self.widgetArchivedVisits.setColumnHidden(0, True) self.widgetArchivedOrderLines.setColumnWidth(0, 30) self.widgetArchivedOrderLines.setColumnWidth(1, 30) self.widgetArchivedOrderLines.setColumnWidth(2, 100) self.widgetArchivedOrderLines.setColumnWidth(3, 150) self.widgetArchivedOrderLines.setColumnWidth(4, 60) self.widgetArchivedOrderLines.setColumnWidth(5, 40) self.widgetCustomers.setColumnHidden(0, True) # ID self.widgetCustomers.setColumnWidth(1, 100) self.widgetCustomers.setColumnWidth(2, 100) self.widgetCustomers.setColumnWidth(3, 100) self.widgetCustomers.setColumnWidth(4, 250) self.widgetCustomers.setColumnWidth(5, 60) self.widgetPricelist.setColumnWidth(0, 70) self.widgetPricelist.setColumnWidth(1, 100) self.widgetPricelist.setColumnWidth(2, 150) self.widgetPricelist.setColumnWidth(3, 50) self.widgetPricelist.setColumnWidth(4, 50) self.widgetPricelist.setColumnWidth(5, 50) self.widgetPricelist.setColumnWidth(6, 50) self.widgetPricelist.setColumnWidth(7, 50) self.widgetPricelist.setColumnWidth(8, 50) self.widgetPricelist.setColumnWidth(9, 50) self.widgetPricelist.setColumnWidth(10, 50) self.widgetPricelist.setColumnWidth(11, 50) self.widgetPricelist.setColumnWidth(12, 50) self.widgetReports.setColumnHidden(0, True) # ID self.widgetReports.setColumnWidth(1, 80) # rep_date self.widgetReports.setColumnWidth(2, 60) # visits self.widgetReports.setColumnWidth(3, 60) # sale day self.widgetReports.setColumnWidth(4, 60) # demo day self.widgetReports.setColumnWidth(5, 100) # turnover day self.widgetReports.setColumnWidth(6, 50) # km # self.widgetReports column 7 # supervisor self.widgetReportVisits.setColumnWidth(0, 150) self.widgetReportVisits.setColumnWidth(1, 100) self.widgetReportVisits.setColumnWidth(2, 100) self.widgetReportVisits.setColumnWidth(3, 60) self.populate_customer_list() self.populate_price_list() try: cid = self._settings.settings["cust_idx"] if self._customers.lookup_by_id(cid): try: self.widgetCustomers.setCurrentIndex( self.widgetCustomers.indexFromItem( self.widgetCustomers.findItems(str( self._customers.customer["customer_id"]), Qt.MatchExactly, column=0)[0])) self.toolButtonCustomer.click() except KeyError: pass except KeyError: return self._reports.load(workdate=self.textWorkdate.text()) self.populate_report_list() self.populate_report_visit_list() self.toolButtonReport.click()
self.data = [] from models.settings import Settings defaults = { 'nuts': ['pages', 'articles'], 'admins': ['Nobody <nobody@localhost>',], 'hosts': ['localhost', 'appspot.com'], 'dp_highlighter': False, 'google_analytics' : '', 'google_adsense_client' : '', 'google_adsense_slot' : '', 'google_adsense_width': 0, 'google_adsense_height': 0, 'disqus_forum' : '' } NutSettings = lambda : Settings.get_or_create('settings:hazel', **defaults) class SettingsForm(Form): nuts = CSVField('Nuts', [], u'A list of your activated modules') # FIXME: create a custom validator that does the validation of the # Name <Email>[, Name <Email>]* field. admins = CSVField('Admins', [], u'A list of people who will be notified in case of emergency') hosts = CSVField('Hosts', [], u'A comma seperated list of hosts on which to listen. In case of a unknown host, a redirect to the first item in the list will occour.') # FIXME: create validator google_analytics = TextField('Google Analytics ID', [], u'UA-xxxxxx-xx') google_adsense_client = TextField('Google AdSense Client', [], u'pub-...') google_adsense_slot = TextField('Google AdSense Slot', [], u'the slot value') google_adsense_width = IntegerField('Google AdSense Width', [], u'the width value') google_adsense_height = IntegerField('Google AdSense Height', [], u'the height value') disqus_forum = TextField('Disqus Forum ID', [], u'The name of the disqus forum to use for comments') dp_highlighter = BooleanField('Enable dp.SyntaxHighlighter')