def test_add(fb): id = "test@test" type = "temperature" bound = 80 direction = "gt" # Delete everything fb.delete(dummy_database, None) # Create new alerts alerts = Alerts(lambda x: x, dummy_database) assert len(alerts.alerts) == 0 # Add one alert alerts.add_alert(id, type, bound, direction) assert len(alerts.alerts) == 1 assert alerts.alerts.ix[0].id == id assert alerts.alerts.ix[0].type == type assert alerts.alerts.ix[0].bound == bound assert alerts.alerts.ix[0].direction == operator.gt if direction == 'gt' else operator.lt # Try adding again alerts.add_alert(id, type, bound, direction) # Nothing should have changed assert len(alerts.alerts) == 1 assert alerts.alerts.ix[0].id == id assert alerts.alerts.ix[0].type == type assert alerts.alerts.ix[0].bound == bound assert alerts.alerts.ix[0].direction == operator.gt if direction == 'gt' else operator.lt
def test_alerts(fb): id = "test@test" type = "temperature" bound = 80 direction = "gt" # Delete everything fb.delete(dummy_database, None) alerts = Alerts(lambda x: x, dummy_database) assert len(alerts.alerts) == 0 alerts.add_alert(id, type, bound, direction) assert len(alerts.alerts) == 1 assert alerts.alerts.ix[0].id == id assert alerts.alerts.ix[0].type == type assert alerts.alerts.ix[0].bound == bound assert alerts.alerts.ix[ 0].direction == operator.gt if direction == 'gt' else operator.lt a = alerts.get_alerts() assert (len(a['alerts']) == 1) assert a['alerts'][0]['id'] == id assert a['alerts'][0]['type'] == type assert a['alerts'][0]['bound'] == bound assert a['alerts'][0]['direction'] == direction
def test_alerts(fb): id = "test@test" type = "temperature" bound = 80 direction = "gt" # Delete everything fb.delete(dummy_database, None) alerts = Alerts(lambda x: x, dummy_database) assert len(alerts.alerts) == 0 alerts.add_alert(id, type, bound, direction) assert len(alerts.alerts) == 1 assert alerts.alerts.ix[0].id == id assert alerts.alerts.ix[0].type == type assert alerts.alerts.ix[0].bound == bound assert alerts.alerts.ix[0].direction == operator.gt if direction == 'gt' else operator.lt a = alerts.get_alerts() assert(len(a['alerts']) == 1) assert a['alerts'][0]['id'] == id assert a['alerts'][0]['type'] == type assert a['alerts'][0]['bound'] == bound assert a['alerts'][0]['direction'] == direction
def getBrightnessPercentage(self): raw_stage = self.getStageInfo() current_stage = raw_stage[0] seconds = raw_stage[1] if current_stage != self.stage: self.logger.info('Scheduled stage changed from \'' + self.stage + '\' to \'' + current_stage + '\'') alerts = Alerts(self.logger) alerts.alertInfo('Scheduled stage changed from \'' + self.stage + '\' to \'' + current_stage + '\'') self.stage = current_stage if self.stage == Stages.pre_sun_rise or self.stage == Stages.post_sun_set: return 0.0 if self.stage == Stages.sun_rise: return float(seconds - self.sunrise_start) / float( self.duration_seconds) if self.stage == Stages.sun_set: return 1 - (float(seconds - self.sunset_start) / float(self.duration_seconds)) return 1.0
def on_disconnect(self, client, userdata, flags, rc=0): self.logger.critical('Connection disconnected, return code: ' + str(rc)) self.happyfish.reconnect_delay = self.happyfish.reconnect_delay * 2 self.connection_closed = True self.time_ended = time() alerts = Alerts(self.logger) alerts.alertCritical(f'RPi disconnected from the MQTT server. RC {rc}')
def __init__(self): self.logger = self.logSetup() alerts = Alerts(self.logger) alerts.alertInfo('Raspberry Pi: Running HappyFish.py') self.mqtt_email = os.environ["MQTT_EMAIL"] self.mqtt_password = os.environ["MQTT_PASSWORD"] self.logger.info('='*50) self.logger.info('Running main script') self.settings = Settings(self.logger, False) self.electronics = Electronics(self.logger, self.settings) self.logger.info('Updating the pwm modules for the first time') self.result = self.electronics.updateModule() self.reconnect_count = 0 self.reconnect_delay = 60 if self.result == True: self.logger.info('PWM modules updated. Electronics working as intended') else: self.logger.critical('Failed to light up the lab room. Check pwm modules') self.logger.critical('Terminating script. Please check hardware') alerts = Alerts(self.logger) alerts.alertCritical('Raspberry Pi: PWM Module cannot be opened') exit() self.connection = Connection(self.logger, self.settings, self.mqtt_email, self.mqtt_password) self.connection.start(self) self.reconnecting = False
def __layout_ui(self): self.log.debug("{0}: Laying out User Interface".format(self)) self.__create_menu() opane = Gtk.VPaned() opane.pack1(self.ilist) opane.pack2(self.view) pane = Gtk.HPaned() pane.pack1(self.tree) pane.pack2(opane) al = Gtk.Alignment.new(0.5,0.5,1,1) al.set_padding(3,3,3,3) al.add(pane) box = Gtk.VBox(spacing=3) box.pack_start(self.ui.get_widget('/Menubar'), False, True, 0) box.pack_start(self.ui.get_widget('/Toolbar'), False, True, 0) #setting GTK3 style ctx= self.ui.get_widget('/Toolbar').get_style_context() print "UI Context: ",ctx ctx.add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR) widget = self.ag.get_action('StopUpdate') widget.set_sensitive(False) box.pack_start(al, True, True, 0) box.pack_start(self.status, False, False, 0) self.add(box) self.set_property("height-request", 700) self.set_property("width-request", 1024) self.is_fullscreen = False self.__reset_title() self.set_icon_from_file(make_path('icons/hicolor','brss.svg')) self.alert = Alerts(self) self.connect("destroy", self.quit) self.show_all() if not self.settings.get_boolean('show-status'): self.status.hide()
def __init__(self): ''' Constructs the GUI ''' self.window = tk.Tk() self.window.title("Crypto Parking") self.window.attributes("-fullscreen", True) # Bind global keyboard commands self.window.bind("<Escape>", self.quit) self.window.config(bg=const.COLOR_BG) self.main_frame = MainFrame(self.window) self.main_frame.pack(side="top", fill="both", expand=True) # Call admin for help button self.call_admin_btn_img = ImageTk.PhotoImage( file="./assets/call_admin_btn.png") self.call_admin_btn = tk.Button(self.frame, width=175, height=35, highlightthickness=0, highlightbackground=const.COLOR_BG, bd=-2, bg=const.COLOR_BG, command=self.call_admin, image=self.call_admin_btn_img) self.call_admin_btn.pack() # Call admin for help button (pressed) self.help_otw_btn_img = ImageTk.PhotoImage( file="./assets/help_otw.png") self.help_otw_btn = tk.Button(self.frame, width=175, height=35, highlightthickness=0, highlightbackground=const.COLOR_BG, bd=-2, bg=const.COLOR_BG, image=self.help_otw_btn_img) # Instantiate alert sender module self.alert_sender = Alerts() #=========================================================================== # CODE USED FOR TESTING # Dummy buttons simulate input '''self.frame = tk.Frame(
def test_add_and_delete(fb): id = "test@test" type = "temperature" bound = 80 direction = "gt" # Delete everything fb.delete(dummy_database, None) alerts = Alerts(lambda x: x, dummy_database) assert len(alerts.alerts) == 0 alerts.add_alert(id, type, bound, direction) assert len(alerts.alerts) == 1 assert alerts.alerts.ix[0].id == id assert alerts.alerts.ix[0].type == type assert alerts.alerts.ix[0].bound == bound assert alerts.alerts.ix[0].direction == operator.gt if direction == 'gt' else operator.lt alerts.delete_alert(id, type, bound, direction) assert len(alerts.alerts) == 0
def test_delete(fb): id = "test@test" type = "temperature" bound = 80 direction = "gt" # Delete everything fb.delete(dummy_database, None) # Add previous data fb.post(dummy_database, { "id": id, "type": type, "bound": bound, "direction": direction }) alerts = Alerts(lambda x: x, dummy_database) # Delete alert alerts.delete_alert(id, type, bound, direction) assert len(alerts.alerts) == 0 # Try deleting alert again alerts.delete_alert(id, type, bound, direction) assert len(alerts.alerts) == 0
def test_add(fb): id = "test@test" type = "temperature" bound = 80 direction = "gt" # Delete everything fb.delete(dummy_database, None) # Create new alerts alerts = Alerts(lambda x: x, dummy_database) assert len(alerts.alerts) == 0 # Add one alert alerts.add_alert(id, type, bound, direction) assert len(alerts.alerts) == 1 assert alerts.alerts.ix[0].id == id assert alerts.alerts.ix[0].type == type assert alerts.alerts.ix[0].bound == bound assert alerts.alerts.ix[ 0].direction == operator.gt if direction == 'gt' else operator.lt # Try adding again alerts.add_alert(id, type, bound, direction) # Nothing should have changed assert len(alerts.alerts) == 1 assert alerts.alerts.ix[0].id == id assert alerts.alerts.ix[0].type == type assert alerts.alerts.ix[0].bound == bound assert alerts.alerts.ix[ 0].direction == operator.gt if direction == 'gt' else operator.lt
def test_delete(fb): id = "test@test" type = "temperature" bound = 80 direction = "gt" # Delete everything fb.delete(dummy_database, None) # Add previous data fb.post(dummy_database, {"id": id, "type": type, "bound": bound, "direction": direction}) alerts = Alerts(lambda x: x, dummy_database) # Delete alert alerts.delete_alert(id, type, bound, direction) assert len(alerts.alerts) == 0 # Try deleting alert again alerts.delete_alert(id, type, bound, direction) assert len(alerts.alerts) == 0
def on_connect(self, client, userdata, flags, rc): self.is_connecting = False if rc == 0: self.established_connection = True self.logger.info('Connected with MQTT broker, result code: ' + str(rc)) self.happyfish.reconnect_delay = 60 alerts = Alerts(self.logger) alerts.alertInfo('Raspberry Pi connected to MQTT successfully') else: self.established_connection = False self.failed_connection = True self.logger.critical('Bad connection, result code: ' + str(rc)) self.happyfish.reconnect_delay = self.happyfish.reconnect_delay * 2 alerts = Alerts(self.logger) alerts.alertCritical( f'Raspberry Pi could NOT connect to MQTT. Bad Connection. RC {rc}' )
def start(self): try: self.logger.info('Running main loop') while True: sleep(0.5) self.electronics.updateModule() if not self.reconnecting and self.connection.connection_closed and self.reconnect_count < 15: self.logger.critical(f'Connection appears to be closed... Ending connection and will reconnect after {self.reconnect_delay/60} min(s)') alerts = Alerts(self.logger) alerts.alertCritical(f'Connection appears to be closed. Reconnecting again in {self.reconnect_delay/60} min(s). Reconnect count is {self.reconnect_count}') self.connection.end() self.reconnecting = True self.timer = Timer(self.reconnect_delay, self.reconnect, args=None, kwargs=None) self.timer.start() except KeyboardInterrupt: self.logger.critical('Script manually terminated') self.connection.end() self.settings.printConfig() self.logger.info('Script ended. Shutting down the lights') self.settings.turnAllOff() self.result = self.electronics.updateModule() if self.result == True: self.logger.info('Successfully turned off all the lights') else: self.logger.critical('Failed to turn off the lights. Unable to communicate with pwm module') alerts = Alerts(self.logger) alerts.alertCritical('HappyFish script got terminated... Unknown reason') self.ended = True
def test_previous_data(fb): id = "test@test" type = "temperature" bound = 80 direction = "gt" # Add previous data fb.post(dummy_database, { "id": id, "type": type, "bound": bound, "direction": direction }) alerts = Alerts(lambda x: x, dummy_database) assert alerts.alerts is not None assert len(alerts.alerts) == 1 assert alerts.alerts.ix[0].id == id assert alerts.alerts.ix[0].type == type assert alerts.alerts.ix[0].bound == bound assert alerts.alerts.ix[ 0].direction == operator.gt if direction == 'gt' else operator.lt
def ia_process(): # Génération du token env_var.Generation_Token() env_var.Generation_Temperature_Moyenne() # Récupération des capteurs => /getSensors sensorsDataResponse = requests.get("https://eclisson.duckdns.org/ConnectedCity/getSensors", headers=env_var.HEADERS) sensorsData = json.loads(sensorsDataResponse.text) # Pour chaque capteur dans la base de données for sensor in sensorsData: print("Sensor ID : " + sensor["sensorID"][0]) # Récupération des executions des capteurs => /getSensors/:id => 062336c2-d39b-42cf-a8bb-1d05de74bd7e rawDataResponse = requests.get("https://eclisson.duckdns.org/ConnectedCity/getRawData/"+sensor["sensorID"][0], headers=env_var.HEADERS) # Vérifie qu'il y a des executions pour ce capteur if rawDataResponse.text != "[]": rawData = json.loads(rawDataResponse.text) rawList = sorted(rawData, key=lambda item: item['time'], reverse=True) # Récupération de la dernière exécution du capteur lastExecution = rawList[0] # s, ms = divmod(int(lastExecution["time"]), 1000) # Vérification des différentes alertes pm25Alert = pm25_execution_process(lastExecution["PM25"]) temperatureAlert = temperature_execution_process(lastExecution["temp"]) co2Alert = co2_execution_process(lastExecution["C02"]) humidityAlert = humidity_execution_process(lastExecution["humidity"]) # Récupération des anciennes alertes pour le capteur en question AlertClass = Alerts() alertDataResponse = requests.get("https://eclisson.duckdns.org/ConnectedCity/getAlerts/"+sensor["sensorID"][0], headers=env_var.HEADERS) if alertDataResponse.text != "[]": # S'il y a déjà des alertes en base pour ce capteur, alors on compare les anciennes alertes avec les nouvelles print("Update des alertes en base de données") alertData = json.loads(alertDataResponse.text) AlertClass.updateAlerts(sensor, alertData[0], co2Alert, pm25Alert, humidityAlert, temperatureAlert) elif alertDataResponse.text == "[]": print("Insertion des alertes en base de données") # S'il n'y a aucune alerte référencée en base de données pour ce capteur, alors on insère en BDD les alertes détectées AlertClass.insertAlerts(sensor, co2Alert, pm25Alert, humidityAlert, temperatureAlert) s.enter(600, 1, ia_process, ())
def test_clear(fb): id = "test@test" type = "temperature" bound = 80 direction = "gt" # Delete everything fb.delete(dummy_database, None) alerts = Alerts(lambda x: x, dummy_database) assert len(alerts.alerts) == 0 alerts.add_alert(id, type, bound, direction) assert len(alerts.alerts) == 1 assert alerts.alerts.ix[0].id == id assert alerts.alerts.ix[0].type == type assert alerts.alerts.ix[0].bound == bound assert alerts.alerts.ix[ 0].direction == operator.gt if direction == 'gt' else operator.lt alerts.clear_alerts() assert len(alerts.alerts) == 0
def alerts_btn(self): root2 = Toplevel(self.master) myGui = Alerts(root2)
temp_c = temp_output particle_output = part.readline().strip() temp_hz, moist_hz = particle_output.split(', ') if temp_c and temp_hz and moist_hz: row = (dt, float(temp_c), float(temp_hz), float(moist_hz)) print "%s, %f, %f, %f" % row output_data.append(row) if float(temp_c) < 23.0: dump_the_pandas_data(output_data) print "Completed Calibration" return start except ValueError: pass except KeyboardInterrupt as e: print "Error:", e return start except Exception as e: dump_the_pandas_data(output_data) return start if __name__ == '__main__': start = read_from_serial() elapsed = datetime.now() - start alert = Alerts(api_keys=settings.pushover_keys) msg = "Calibration program done. Time to complete: " + str(elapsed) print(msg) alert.send_pushover(msg) exit()
class GUI(object): ''' GUI module of our application Responsible for construction of the gui and handling interactions\ Has the right to set the user wants to pay flag ''' def __init__(self): ''' Constructs the GUI ''' self.window = tk.Tk() self.window.title("Crypto Parking") self.window.attributes("-fullscreen", True) # Bind global keyboard commands self.window.bind("<Escape>", self.quit) self.window.config(bg=const.COLOR_BG) self.main_frame = MainFrame(self.window) self.main_frame.pack(side="top", fill="both", expand=True) # Call admin for help button self.call_admin_btn_img = ImageTk.PhotoImage( file="./assets/call_admin_btn.png") self.call_admin_btn = tk.Button(self.frame, width=175, height=35, highlightthickness=0, highlightbackground=const.COLOR_BG, bd=-2, bg=const.COLOR_BG, command=self.call_admin, image=self.call_admin_btn_img) self.call_admin_btn.pack() # Call admin for help button (pressed) self.help_otw_btn_img = ImageTk.PhotoImage( file="./assets/help_otw.png") self.help_otw_btn = tk.Button(self.frame, width=175, height=35, highlightthickness=0, highlightbackground=const.COLOR_BG, bd=-2, bg=const.COLOR_BG, image=self.help_otw_btn_img) # Instantiate alert sender module self.alert_sender = Alerts() #=========================================================================== # CODE USED FOR TESTING # Dummy buttons simulate input '''self.frame = tk.Frame( self.window, bg=const.COLOR_BG ) self.frame.pack( side="bottom", fill="x", expand=False, pady=15 ) self.confirm = tk.Button( self.frame, text="Confirm paid", width=16, command=self.confirm ) self.s1 = tk.Button( self.frame, text="Sensor 1", width=16, command=self.s1 ) self.s0 = tk.Button( self.frame, text="Sensor 0", width=16, command=self.s0 ) #self.confirm.pack() #self.s1.pack() #self.s0.pack() def s1(self): SV.sensor_detected = True def s0(self): SV.sensor_detected = False def confirm(self): SV.payment_received = True ''' # CODE USED FOR TESTING #=========================================================================== def run(self): ''' Runs the GUI ''' self.show_main_page() self.window.mainloop() def show_main_page(self): ''' Switch to main page ''' # set the rate info in USD using exchanghe rate API self.set_usd_rate() self.main_frame.welcome_page.lift() # in case when main page is shown after the thank you page expires # will unblock execution of state transition from await payment to # free parking in main loop SV.E_thankyou_page.set() #*************************************************************************** def show_parked_page(self): ''' Switch to parked page ''' self.main_frame.parked_page.lift() def show_pay_page(self): ''' Switch to pay page ''' self.main_frame.pay_page.lift() def show_paid_page(self): ''' Switch to thank you page ''' #*************************************************************************** # Block execution of state transition from await payment to # free parking in main loop to wait for thank you page to expire SV.E_thankyou_page.clear() self.main_frame.paid_page.lift() #----------------------------------------------------------------------- # THREAD: Show main page again after a set time SV.threads['thank_you_page'] = threading.Timer(2, self.show_main_page) SV.threads['thank_you_page'].start() def set_pay_text(self, amount, time): ''' Sets the amount due information in the pay page Sets the text of the payment due in USD using exchange rate API amount: payment due in BTC time: total time parked in seconds ''' # Get exchange rate from BTC to USD from API try: # Call API res = requests.get(const.EXCHANGE_RATE_API).json() # Get relevant information from response USD_per_BTC = float(res['last']) # calculate the amount due in USD amount_usd = USD_per_BTC * amount amount_usd_text = "($%.2f)" % amount_usd # If network / API is not available, show error test on GUI except (requests.exceptions.RequestException, ConnectionError) as e: print("API ERROR") print(e) amount_usd_text = "Network error, can't fetch exchange rate" self.main_frame.pay_page.amount_due_usd.set(amount_usd_text) self.main_frame.pay_page.amount_due.set("%.6f BTC" % amount) self.main_frame.pay_page.amount_due.set("%.6f BTC" % amount) self.main_frame.pay_page.time_parked.set("%.2f seconds" % time) def set_usd_rate(self): ''' Sets the parking rate in USD text using exchange rate from API ''' try: # Call API res = requests.get(const.EXCHANGE_RATE_API).json() # Get relevant info from response USD_per_BTC = float(res['last']) # Conversion rate_float_hr = const.PARKING_RATE * USD_per_BTC * 3600.0 text = "($%.2f / hr)" % rate_float_hr # If network / API is not available, show error test on GUI except (requests.exceptions.RequestException, ConnectionError) as e: print(e) text = "Network error, can't fetch exchange rate" self.main_frame.welcome_page.parking_rate_usd.set(text) def quit(self, instance=None): ''' Destroys GUI to quit application, sets flag to break main loop ''' self.window.destroy() SV.KILL = True def call_admin(self): ''' Uses the alerts module to call admin in case user needs assistance Also changes the button appearance to give user feedback that admin has been notified When the appearance is changed, button taps will not send notification again ''' self.alert_sender.send_user_alert() self.call_admin_btn.pack_forget() self.help_otw_btn.pack() # After a set time, revert the appearance of the button SV.threads['help_btn'] = threading.Timer(10, self.revert_call_admin_btn) SV.threads['help_btn'].start() def revert_call_admin_btn(self): ''' Reverts the call admin button appearnce ''' self.help_otw_btn.pack_forget() self.call_admin_btn.pack()
send_email(to=to, subject=subject, message=message) logger = logging.getLogger('alerts') logger.setLevel(logging.INFO) ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) logger.addHandler(ch) # Set up web server app = Flask(__name__) CORS(app) data, get_more_data = get_data() # Set up alerts alerts = Alerts(triggered_alerts) threading.Thread(target=alerts.run).start() @app.route("/sensor/status") def status(): data = get_more_data() response = {"last_reading": data.ix[-1].name.value // 10**6} return jsonify(**response) @app.route("/sensor/summary") def summary(): data = get_more_data()
def main(): """ - Parse user-specified data from YaML - Check to see that the needed graphics are available. If not, get them. - Get the radar imagery, complete with warnings graphics - Get today's hazardous weather outlook statement and parse it - Check for FTM outage notifications - Get, parse, and write out current weather conditions to specified locations. - TODO: should run the getweather.sh shell script, that overlays/composites the weather graphics. At present, that shell script calls this script and runs the overlays with -bash-. - Check for and acquire current multi-band GOES-x imagery of a given resolution. """ if os.path.exists('weatherwidget.log'): os.remove('weatherwidget.log') logging.basicConfig( filename='weatherwidget.log', level=logging.DEBUG, format='%(asctime)s %(levelname)s %(threadName)-10s %(message)s', ) data = wf.load_settings_and_defaults(SETTINGS_DIR, 'settings.yml', 'defaults.yml') if not data: logging.error('Unable to load settings files. These are required.') sys.exit( 'settings files are required and could not be loaded successfully.' ) logging.info('Checking for radar outage.') wf.outage_check(data) logging.info('Retrieving current weather observations.') right_now = Observation(data) right_now.get_current_conditions() right_now.get_backup_obs(use_json=False) right_now.merge_good_observations() logging.debug('Merged current conditions: %s', right_now.con1.obs) sum_con = right_now.conditions_summary() if right_now.con1.obs and sum_con: text_conditions, nice_con = right_now.format_current_conditions() logging.debug('Current conditions from primary source: %s', nice_con) wf.write_json(some_dict=nice_con, outputdir=data['output_dir'], filename='current_conditions.json') else: logging.error( 'Something went wrong getting the current conditions. Halting.') return 1 wf.write_text(os.path.join(data['output_dir'], 'current_conditions.txt'), text_conditions) # Get radar image: current_radar = Radar(data) current_radar.check_assets() current_radar.get_radar() current_radar.get_warnings_box() if current_radar.problem: logging.error('Unable to retrieve weather radar image. Halting now.') # Hazardous Weather Outlook and alerts: today_alerts = Alerts(data) today_alerts.get_alerts() # Get hydrograph image. if wf.get_hydrograph(abbr=data['river_gauge_abbr'], hydro_url=data['defaults']['water_url'], outputdir=data['output_dir']).ok: logging.info('Requesting hydrograph for station %s, gauge "%s".', data['radar_station'], data['river_gauge_abbr']) else: logging.error('Failed to get hydrograph information.') return 1 forecast_obj = Forecast(data=data) logging.debug('Getting the forecasts.') forecast_obj.get_forecast() forecastdict = forecast_obj.parse_forecast() if forecastdict is None: logging.error('Unable to parse forecast!') return 1 forecast_obj.write_forecast(outputdir=data['output_dir']) logging.debug('Getting area forecast discussion.') forecast_obj.get_afd() logging.debug('Getting zone forecast.') zoneforecast = ZoneForecast(data) zoneforecast.get() wf.write_json(some_dict=forecastdict, outputdir=data['output_dir'], filename='forecast.json') wsvg.make_forecast_icons(forecastdict, outputdir=data['output_dir']) # Satellite imagery: current_image = Imagery(band='GEOCOLOR', data=data) current_image.get_all() logging.info('Finished program run.') return 0
class Reader (Gtk.Window, GObject.GObject): """ """ __gsignals__ = { "loaded" : ( GObject.SignalFlags.RUN_FIRST, None, ()), "next-article" : ( GObject.SignalFlags.RUN_FIRST, None, ()), "previous-article" : ( GObject.SignalFlags.RUN_FIRST, None, ()), "next-feed" : ( GObject.SignalFlags.RUN_FIRST, None, ()), "previous-feed" : ( GObject.SignalFlags.RUN_FIRST, None, ()), "no-engine" : ( GObject.SignalFlags.RUN_FIRST, None, ()), } def __repr__(self): return "Reader" def __init__(self): Gtk.Window.__init__(self) GObject.GObject.__init__(self) GObject.type_register(Reader) self.log = Logger("reader.log", "BRss-Reader") self.settings = Gio.Settings.new(BASE_KEY) self.settings.connect("changed::show-status", self.__toggle_status) # ui elements self.tree = Tree(self.log) self.ilist = ArticleList(self.log) self.ilist.set_property("height-request", 250) self.view = View(self.log) self.status = Status() # layout self.__layout_ui() #signals self.__connect_signals() self.__get_engine() # ready to go self.emit('loaded') def __check_engine(self): bus = dbus.SessionBus() try: engine = bus.get_object(ENGINE_DBUS_KEY, ENGINE_DBUS_PATH) except: return False return engine def __get_engine(self): self.engine = self.__check_engine() if not self.engine: self.log.critical("{0}: Couldn't get a DBus connection; quitting.".format(self)) self.alert.error(_("Could not connect to Engine"), _("BRss will now quit.\nPlease make sure that the engine is running and restart the application")) self.quit() self.create = self.engine.get_dbus_method('create', ENGINE_DBUS_KEY) self.edit = self.engine.get_dbus_method('edit', ENGINE_DBUS_KEY) self.update = self.engine.get_dbus_method('update', ENGINE_DBUS_KEY) self.delete = self.engine.get_dbus_method('delete', ENGINE_DBUS_KEY) self.get_menu_items = self.engine.get_dbus_method('get_menu_items', ENGINE_DBUS_KEY) self.get_articles_for = self.engine.get_dbus_method('get_articles_for', ENGINE_DBUS_KEY) self.search_for = self.engine.get_dbus_method('search_for', ENGINE_DBUS_KEY) self.get_article = self.engine.get_dbus_method('get_article', ENGINE_DBUS_KEY) self.toggle_starred = self.engine.get_dbus_method('toggle_starred', ENGINE_DBUS_KEY) self.toggle_read = self.engine.get_dbus_method('toggle_read', ENGINE_DBUS_KEY) self.stop_update = self.engine.get_dbus_method('stop_update', ENGINE_DBUS_KEY) self.count_special = self.engine.get_dbus_method('count_special', ENGINE_DBUS_KEY) self.get_configs = self.engine.get_dbus_method('get_configs', ENGINE_DBUS_KEY) self.set_configs = self.engine.get_dbus_method('set_configs', ENGINE_DBUS_KEY) self.import_opml = self.engine.get_dbus_method('import_opml', ENGINE_DBUS_KEY) self.export_opml = self.engine.get_dbus_method('export_opml', ENGINE_DBUS_KEY) self.rag.set_visible(False) self.ag.set_visible(True) self.__connect_engine_signals() self.log.debug(_("{0}: Connected to feed engine {1}".format(self, self.engine))) def __create_menu(self): ui_string = """<ui> <menubar name='Menubar'> <menu action='FeedMenu'> <menuitem action='New feed'/> <menuitem action='New category'/> <menuitem action='Delete'/> <separator/> <menuitem action='Import feeds'/> <menuitem action='Export feeds'/> <separator /> <menuitem name='Reconnect' action='Reconnect'/> <separator/> <menuitem action='Quit'/> </menu> <menu action='EditMenu'> <menuitem action='Edit'/> <menuitem action='Find'/> <separator/> <menuitem action='Star'/> <menuitem action='Read'/> <separator/> <menuitem action='Preferences'/> </menu> <menu action='NetworkMenu'> <menuitem action='Update'/> <menuitem action='Update all'/> </menu> <menu action='ViewMenu'> <menuitem action='NextArticle'/> <menuitem action='PreviousArticle'/> <menuitem action='NextFeed'/> <menuitem action='PreviousFeed'/> <separator /> <menuitem action='FullScreen'/> </menu> <menu action='HelpMenu'> <menuitem action='About'/> </menu> </menubar> <toolbar name='Toolbar'> <toolitem name='New feed' action='New feed'/> <toolitem name='New category' action='New category'/> <separator name='sep1'/> <toolitem name='Reconnect' action='Reconnect'/> <separator /> <toolitem name='Update all' action='Update all'/> <toolitem name='Stop' action='StopUpdate'/> <separator name='sep2'/> <toolitem name='PreviousFeed' action='PreviousFeed'/> <toolitem name='PreviousArticle' action='PreviousArticle'/> <toolitem name='Star' action='Star'/> <toolitem name='NextArticle' action='NextArticle'/> <toolitem name='NextFeed' action='NextFeed'/> <separator name='sep3'/> <toolitem name='FullScreen' action='FullScreen'/> <toolitem name='Find' action='Find'/> <toolitem name='Preferences' action='Preferences'/> </toolbar> </ui>""" self.mag = Gtk.ActionGroup('MenuActions') mactions = [ ('FeedMenu', None, _('_Feeds')), ('EditMenu', None, _('E_dit')), ('NetworkMenu', None, _('_Network')), ('ViewMenu', None, _('_View')), ('HelpMenu', None, _('_Help')), ('About', "gtk-about", _('_About'), None, _('About'), self.__about), ] self.mag.add_actions(mactions) self.ag = Gtk.ActionGroup('WindowActions') actions = [ ('New feed', 'feed', _('_New feed'), '<control><alt>n', _('Add a feed'), self.__add_feed), ('New category', "gtk-directory", _('New _category'), '<control><alt>c', _('Add a category'), self.__add_category), ('Delete', "gtk-clear", _('Delete'), 'Delete', _('Delete a feed or a category'), self.__delete_item), ('Import feeds', "gtk-redo", _('Import feeds'), None, _('Import a feedlist'), self.__import_feeds), ('Export feeds', "gtk-undo", _('Export feeds'), None, _('Export a feedlist'), self.__export_feeds), ('Quit', "gtk-quit", _('_Quit'), '<control>Q', _('Quits'), self.quit), ('Edit', "gtk-edit", _('_Edit'), '<control>E', _('Edit the selected element')), ('Star', "gtk-about", _('_Star'), 'x', _('Star the current article'), self.__star), ('Read', "gtk-ok", _('_Read'), 'r', _('Toggle the current article read status'), self.__read), ('Preferences', "gtk-preferences", _('_Preferences'), '<control>P', _('Configure the engine'), self.__edit_prefs), ('Update', None, _('_Update'), '<control>U', _('Update the selected feed'), self.__update_feed), ('Update all', "gtk-refresh", _('Update all'), '<control>R', _('Update all feeds'), self.__update_all), ('PreviousArticle', "gtk-go-back", _('Previous Article'), 'b', _('Go to the previous article'), self.__previous_article), ('NextArticle', "gtk-go-forward", _('Next Article'), 'n', _('Go to the next article'), self.__next_article), ('PreviousFeed', "gtk-goto-first", _('Previous Feed'), '<shift>b', _('Go to the previous news feed'), self.__previous_feed), ('NextFeed', "gtk-goto-last", _('Next Feed'), '<shift>n', _('Go to the next news feed'), self.__next_feed), ('StopUpdate', "gtk-stop", _('Stop'), None, _('Stop the current update'), self.__stop_updates), ] tactions = [ ('Find', "gtk-find", _('Find'), '<control>F', _('Search for a term in the articles'), self.__toggle_search), ('FullScreen', "gtk-fullscreen", _('Fullscreen'), 'F11', _('(De)Activate fullscreen'), self.__toggle_fullscreen), ] self.ag.add_actions(actions) self.ag.add_toggle_actions(tactions) # break reconnect into its own group self.rag = Gtk.ActionGroup("Rec") ractions = [ ('Reconnect', "gtk-disconnect", _('_Reconnect'), None, _('Try and reconnect to the feed engine'), self.__reconnect), ] self.rag.add_actions(ractions) self.ui = Gtk.UIManager() self.ui.insert_action_group(self.mag, 0) self.ui.insert_action_group(self.ag, 0) self.ui.insert_action_group(self.rag, 1) self.ui.add_ui_from_string(ui_string) self.add_accel_group(self.ui.get_accel_group()) def __reset_title(self, *args): self.set_title('BRss Reader') def __stop_updates(self, *args): self.stop_update( reply_handler=self.__to_log, error_handler=self.__to_log) self.ag.get_action('StopUpdate').set_sensitive(False) def __layout_ui(self): self.log.debug("{0}: Laying out User Interface".format(self)) self.__create_menu() opane = Gtk.VPaned() opane.pack1(self.ilist) opane.pack2(self.view) pane = Gtk.HPaned() pane.pack1(self.tree) pane.pack2(opane) al = Gtk.Alignment.new(0.5,0.5,1,1) al.set_padding(3,3,3,3) al.add(pane) box = Gtk.VBox(spacing=3) box.pack_start(self.ui.get_widget('/Menubar'), False, True, 0) box.pack_start(self.ui.get_widget('/Toolbar'), False, True, 0) #setting GTK3 style ctx= self.ui.get_widget('/Toolbar').get_style_context() print "UI Context: ",ctx ctx.add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR) widget = self.ag.get_action('StopUpdate') widget.set_sensitive(False) box.pack_start(al, True, True, 0) box.pack_start(self.status, False, False, 0) self.add(box) self.set_property("height-request", 700) self.set_property("width-request", 1024) self.is_fullscreen = False self.__reset_title() self.set_icon_from_file(make_path('icons/hicolor','brss.svg')) self.alert = Alerts(self) self.connect("destroy", self.quit) self.show_all() if not self.settings.get_boolean('show-status'): self.status.hide() def __connect_signals(self): # signals self.log.debug("{0}: Connecting all signals".format(self)) self.connect('next-article', self.ilist.next_item) self.connect('previous-article', self.ilist.previous_item) self.connect('next-feed', self.tree.next_item) self.connect('previous-feed', self.tree.previous_item)#TODO: implement self.connect_after('no-engine', self.__no_engine) self.connect_after('no-engine', self.view.no_engine) self.tree.connect('item-selected', self.__load_articles) self.tree.connect('dcall-request', self.__handle_dcall) self.ilist.connect('item-selected', self.__load_article) self.ilist.connect('item-selected', self.__update_title) self.ilist.connect('no-data', self.view.clear) self.ilist.connect('no-data', self.__reset_title) self.ilist.connect('star-toggled', self.__toggle_starred) self.ilist.connect('read-toggled', self.__toggle_read) self.ilist.connect('filter-ready', self.__connect_accels) self.ilist.connect('list_loaded', self.__hide_search,) self.ilist.connect_after('row-updated', self.tree.update_starred) self.ilist.connect_after('row-updated', self.tree.update_unread) self.ilist.connect('dcall-request', self.__handle_dcall) self.ilist.connect('search-requested', self.__search_articles) self.ilist.connect('search-requested', self.tree.deselect) self.view.connect('article-loaded', self.ilist.mark_read) self.view.connect('link-clicked', self.__to_browser) self.view.connect('link-hovered-in', self.__status_info) self.view.connect('link-hovered-out', self.__status_info) def __connect_engine_signals(self): self.engine.connect_to_signal('notice', self.status.message) self.engine.connect_to_signal('added', self.__handle_added) self.engine.connect_to_signal('updated', self.__handle_updated) self.engine.connect_to_signal('updating', self.__update_started) self.engine.connect_to_signal('complete', self.__update_done) self.engine.connect_to_signal('complete', self.tree.select_current) # might want to highlight these a bit more self.engine.connect_to_signal('warning', self.status.warning) def __import_feeds(self, *args): dialog = Gtk.FileChooserDialog(_("Open..."), self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_default_response(Gtk.ResponseType.OK) filter = Gtk.FileFilter() filter.set_name("opml/xml") filter.add_pattern("*.opml") filter.add_pattern("*.xml") dialog.add_filter(filter) filter = Gtk.FileFilter() filter.set_name(_("All files")) filter.add_pattern("*") dialog.add_filter(filter) response = dialog.run() filename = dialog.get_filename() dialog.destroy() if response == Gtk.ResponseType.OK: self.log.debug("{0}: Trying to import from OPML file {1}".format(self, filename)) self.import_opml(filename, reply_handler=self.__to_log, error_handler=self.__to_log) def __export_feeds(self, *args): dialog = Gtk.FileChooserDialog(_("Save..."), self, Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) dialog.set_default_response(Gtk.ResponseType.OK) dialog.set_do_overwrite_confirmation(True) dialog.set_current_name("brss.opml") filter = Gtk.FileFilter() filter.set_name("opml/xml") filter.add_pattern("*.opml") filter.add_pattern("*.xml") dialog.add_filter(filter) response = dialog.run() filename = dialog.get_filename() dialog.destroy() if response == Gtk.ResponseType.OK: self.log.debug("{0}: Trying to export to OPML file {1}".format(self, filename)) self.export_opml(filename, reply_handler=self.__to_log, error_handler=self.__to_log) def __populate_menu(self, *args): self.log.debug("{0}: Populating menu".format(self)) self.get_menu_items( reply_handler=self.tree.fill_menu, error_handler=self.__to_log) self.count_special( reply_handler=self.tree.make_special_folders, error_handler=self.__to_log) def __toggle_starred(self, ilist, item): self.toggle_starred(item, reply_handler=self.__to_log, error_handler=self.__to_log) def __toggle_read(self, ilist, item): self.toggle_read(item, reply_handler=self.__to_log, error_handler=self.__to_log) def __load_articles(self, tree, item): self.log.debug("{0}: Loading articles for feed {1}".format(self, item['name'])) self.get_articles_for(item, reply_handler=self.ilist.load_list, error_handler=self.__to_log) def __add_category(self, *args): args = [ {'type':'str','name':'name', 'header':_('Name') }, ] d = Dialog(self, _('Add a category'), args) r = d.run() item = d.response item['type'] = 'category' d.destroy() if r == Gtk.ResponseType.OK: self.__create(item) def __add_feed(self, *args): data = [ {'type':'str','name':'url', 'header':_('Link') }, ] d = Dialog(self, _('Add a feed'), data) r = d.run() item = d.response item['type'] = 'feed' d.destroy() if r == Gtk.ResponseType.OK: self.__create(item) def __edit_prefs(self, *args): kmap = { 'hide-read':'bool', 'update-interval':'int', 'max-articles':'int', 'use-notify':'bool', 'on-the-fly':'bool', 'enable-debug':'bool', 'auto-update':'bool', 'live-search':'bool', 'auto-hide-search':'bool', 'show-status':'bool', } hmap = { 'hide-read':_('Hide Read Items'), 'update-interval':_('Update interval (in minutes)'), 'max-articles':_('Maximum number of articles to keep (excluding starred)'), 'auto-update':_('Allow the engine to download new articles automatically.'), 'on-the-fly':_('Start downloading articles for new feeds on-the-fly'), 'use-notify':_('Show notification on updates'), 'enable-debug':_('Enable detailed logs'), 'live-search':_('Return search results as you type'), 'auto-hide-search':_('Hide Search form on results'), 'show-status':_('Show the bottom status bar'), } data = [] for k,v in kmap.iteritems(): data.append({ 'type':v, 'name':k, 'header':hmap.get(k),#FIXME: can this be gotten from gsettings? }) d = Dialog(self, _('Edit preferences'), data, self.settings) r = d.run() d.destroy() def __about(self, *args): """Shows the about message dialog""" LICENSE = """ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ about = Gtk.AboutDialog() about.set_transient_for(self) about.set_program_name("BRss") about.set_version(__version__) about.set_authors(__maintainers__) about.set_artists(__maintainers__) about.set_copyright("(c) 2011 ITGears") about.set_license(LICENSE) about.set_comments(_("BRss is an offline DBus-based RSS reader")) about.set_logo(make_pixbuf('brss')) about.run() about.destroy() def __create(self, item): self.log.debug("About to create item: {0}".format(item)) self.create(item, reply_handler=self.__to_log, error_handler=self.__to_log) def __edit_item(self, *args): item = self.tree.current_item #FIXME: not good!! print item if item['type'] == 'category': args = [{'type':'str','name':'name', 'header':_('Name'), 'value':item['name'] },] d = Dialog(self, _('Edit this category'), args) elif item['type'] == 'feed': args = [{'type':'str','name':'url', 'header':_('Link'), 'value':item['url'] },] d = Dialog(self, _('Edit this feed'), args) r = d.run() o = d.response for k,v in o.iteritems(): item[k] = v d.destroy() if r == Gtk.ResponseType.OK: self.__edit(item) def __edit(self, item): self.log.debug("About to edit item: {0}".format(item)) self.edit(item, reply_handler=self.__to_log, error_handler=self.__to_log) def __update(self, item): self.log.debug("{0} Requesting update for: {1}".format(self, item)) self.update(item, reply_handler=self.__to_log, error_handler=self.__to_log) def __update_all(self, *args): self.__update('all') def __update_feed(self, item): self.__update(self.tree.current_item) def __delete_item(self, *args): self.__delete(self.tree.current_item) def __delete(self, item): self.log.debug("About to delete item: {0}".format(item)) self.alert.question(_("Are you sure you want to delete this {0} ?".format(item['type'])), _("All included feeds and articles will be deleted.") ) if self.alert.checksum: self.log.debug("Deletion confirmed") self.delete(item, reply_handler=self.__populate_menu, error_handler=self.__to_log) def __load_article(self, ilist, item): self.get_article(item, reply_handler=self.view.show_article, error_handler=self.__to_log) def __handle_added(self, item): self.log.debug("{0}: Added item: {1}".format(self, item['id'])) if item['type'] in ['feed', 'category']: return self.tree.insert_row(item) if item['type'] == 'article': return self.ilist.insert_row(item) def __handle_updated(self, item): #~ self.log.debug("{0}: Updated item: {1}".format(self, item['id'])) if item['type'] in ['feed', 'category']: return self.tree.update_row(item) if item['type'] == 'article': self.view.star_this(item) return self.ilist.update_row(item) def __handle_dcall(self, caller, name, item): if name in [_('Update'), _('Update all')]: self.log.debug("updating {0}".format(item)) self.update(item, reply_handler=self.__update_done, error_handler=self.__to_log) elif name == _('Mark all as read'): self.ilist.mark_all_read() elif name == _('Open in Browser'): self.__to_browser(caller, item['url']) elif name == _('Copy Url to Clipboard'): self.__to_clipboard(item['url']) elif name in [_('Delete'), _('Delete Feed'), _('Delete Category')]: self.__delete(item) elif name == _('Edit'): self.__edit_item(item) def __search_articles(self, caller, string): self.log.debug("Searching articles with: {0}".format(string.encode('utf-8'))) self.search_for(string, reply_handler=self.ilist.load_list, error_handler=self.__to_log) def __toggle_in_update(self, b): gmap = {True:False, False:True} a = self.ag.get_action('StopUpdate') a.set_sensitive(b) a = self.ag.get_action('Update all') a.set_sensitive(gmap.get(b)) def __toggle_search(self, *args): # show ilist if in fullscreen self.ilist.toggle_search() if self.ilist.search_on: if self.is_fullscreen: self.ilist.show() else: if self.is_fullscreen: self.ilist.hide() else: self.ilist.listview.grab_focus() def __hide_search(self, *args): if not self.settings.get_boolean('live-search'): if self.ilist.search_on and self.settings.get_boolean('auto-hide-search'): w = self.ag.get_action('Find') if w.get_sensitive(): w.activate() def __star(self, *args): self.ilist.mark_starred() def __read(self, *args): self.ilist.toggle_read() def __update_started(self, *args): self.log.debug("Toggling update status to True") self.__toggle_in_update(True) def __update_done(self, *args): self.log.debug("Toggling update status to False") self.__toggle_in_update(False) def __update_title(self, caller, item): self.set_title(item['title']) def __status_info(self, caller, message=None): if message: self.status.message('info', message) else: self.status.clear() def __to_log(self, *args): for a in args: self.log.warning(a) if type(a) == dbus.exceptions.DBusException: self.emit('no-engine') def __to_browser(self, caller, link): self.log.debug("Trying to open link '{0}' in browser".format(link)) orig_link = self.view.link_button.get_uri() self.view.link_button.set_uri(link) self.view.link_button.activate() self.view.link_button.set_uri(orig_link) def __previous_article(self, *args): self.emit('previous-article') def __next_article(self, *args): self.emit('next-article') def __previous_feed(self, *args): self.emit('previous-feed') def __next_feed(self, *args): self.emit('next-feed') def __to_clipboard(self, link): clipboard = Gtk.Clipboard() clipboard.set_text(link.encode("utf8"), -1) clipboard.store() def __connect_accels (self, widget): widget.filterentry.connect('focus-in-event', self.__toggle_accels, False) widget.filterentry.connect('focus-out-event', self.__toggle_accels, True) widget.filterentry.grab_focus() def __toggle_accels(self, widget, event, b): n = self.ag.get_action('NextArticle') nf = self.ag.get_action('NextFeed') p = self.ag.get_action('PreviousArticle') pf = self.ag.get_action('PreviousFeed') s = self.ag.get_action('Star') r = self.ag.get_action('Read') if b: self.log.debug("Toggling accels on") for acc in [n, nf, p, pf, s, r]: acc.connect_accelerator() else: self.log.debug("Toggling accels off") for acc in [n, nf, p, pf, s, r]: acc.disconnect_accelerator() def __toggle_status(self, settings, key=None): if settings.get_boolean(key): self.log.debug('{0}: showing the status bar'.format(self)) self.status.show() else: self.log.debug('{0}: hiding the status bar'.format(self)) self.status.hide() def __toggle_fullscreen(self, *args): if self.is_fullscreen == True: self.ilist.show() self.tree.show() self.unfullscreen() self.is_fullscreen = False else: self.ilist.hide() self.tree.hide() self.fullscreen() self.is_fullscreen = True def __reconnect(self, *args): self.log.warning("Trying to reconnect to engine!") self.__get_engine() self.emit('loaded') def __no_engine(self, *args): self.log.warning("Lost connection with engine!") self.status.message('critical', _("Cannot connect to the Feed Engine")) self.log.debug("Showing reconnect icon!") self.rag.set_visible(True) self.ag.set_visible(False) self.__update_done() #~ def __feed_selected(self, caller, item): #~ self.status.message('info', "{0}".format( #~ item['name'].encode('utf-8'))) def quit(self, *args): self.destroy() def start(self, *args): self.present() def do_loaded(self, *args): self.log.debug("Starting BRss Reader") self.__populate_menu() self.status.message('ok', _('Connected to engine'))
def test_init(fb): # Delete everything fb.delete(dummy_database, None) alerts = Alerts(lambda x: x, dummy_database) assert len(alerts.alerts) == 0