Merge pull request #161 from facetoe/master

Assorted bits and pieces
This commit is contained in:
enkore 2015-01-20 21:01:15 +01:00
commit 7c3f545438
6 changed files with 229 additions and 28 deletions

View File

@ -30,6 +30,7 @@ MOCK_MODULES = [
"i3pystatus.pulseaudio.pulse",
"notmuch",
"requests",
"bs4"
]
for mod_name in MOCK_MODULES:

View File

@ -439,7 +439,16 @@ def user_open(url_or_command):
scheme = urlparse(url_or_command).scheme
if scheme == 'http' or scheme == 'https':
import webbrowser
webbrowser.open(url_or_command)
import os
# webbrowser.open() sometimes prints a message for some reason and confuses i3
# Redirect stdout briefly to prevent this from happening.
savout = os.dup(1)
os.close(1)
os.open(os.devnull, os.O_RDWR)
try:
webbrowser.open(url_or_command)
finally:
os.dup2(savout, 1)
else:
import subprocess
subprocess.Popen(url_or_command, shell=True)

59
i3pystatus/github.py Normal file
View File

@ -0,0 +1,59 @@
from i3pystatus import IntervalModule
import requests
import json
from i3pystatus.core import ConfigError
from i3pystatus.core.util import user_open, internet, require
class Github(IntervalModule):
"""
Check Github for pending notifications.
Requires `requests`
Formatters:
* `{unread}` - contains the value of unread_marker when there are pending notifications
* `{unread_count}` - number of unread notifications, empty if 0
"""
unread_marker = u""
unread = ''
color = '#78EAF2'
username = ''
password = ''
format = '{unread}'
interval = 600
on_leftclick = 'open_github'
settings = (
('format', 'format string'),
('unread_marker', 'sets the string that the "unread" formatter shows when there are pending notifications'),
("username", ""),
("password", ""),
("color", "")
)
def open_github(self):
user_open('https://github.com/' + self.username)
@require(internet)
def run(self):
format_values = dict(unread_count='', unread='')
response = requests.get('https://api.github.com/notifications', auth=(self.username, self.password))
data = json.loads(response.text)
# Bad credentials
if isinstance(data, dict):
raise ConfigError(data['message'])
unread = len(data)
if unread > 0:
format_values['unread_count'] = unread
format_values['unread'] = self.unread_marker
self.output = {
'full_text': self.format.format(**format_values),
'color': self.color
}

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
import netifaces
import basiciw
import psutil
from i3pystatus import IntervalModule
from i3pystatus.core.color import ColorRangeModule
from i3pystatus.core.util import make_graph, round_dict, make_bar
@ -74,7 +72,7 @@ class NetworkInfo():
Retrieve network information.
"""
def __init__(self, interface, ignore_interfaces, detached_down, unknown_up):
def __init__(self, interface, ignore_interfaces, detached_down, unknown_up, get_wifi_info=False):
if interface not in netifaces.interfaces() and not detached_down:
raise RuntimeError(
"Unknown interface {iface}!".format(iface=interface))
@ -82,6 +80,7 @@ class NetworkInfo():
self.ignore_interfaces = ignore_interfaces
self.detached_down = detached_down
self.unknown_up = unknown_up
self.get_wifi_info = get_wifi_info
def get_info(self, interface):
format_dict = dict(v4="", v4mask="", v4cidr="", v6="", v6mask="", v6cidr="")
@ -133,9 +132,15 @@ class NetworkInfo():
break
return info
@staticmethod
def extract_wireless_info(interface):
def extract_wireless_info(self, interface):
info = dict(essid="", freq="", quality=0.0, quality_bar="")
# Just return empty values if we're not using any Wifi functionality
if not self.get_wifi_info:
return info
import basiciw
try:
iwi = basiciw.iwinfo(interface)
except Exception:
@ -169,6 +174,8 @@ class NetworkTraffic():
self.round_size = round_size
def update_counters(self, interface):
import psutil
self.pnic_before = self.pnic
counters = psutil.net_io_counters(pernic=True)
self.pnic = counters[interface] if interface in counters else None
@ -282,24 +289,41 @@ class Network(IntervalModule, ColorRangeModule):
on_leftclick = "nm-connection-editor"
on_rightclick = "cycle_interface"
on_upscroll = ['cycle_interface', 1]
on_downscroll = ['cycle_interface', -1]
def init(self):
self.network_traffic = NetworkTraffic(self.unknown_up, self.divisor, self.round_size)
self.network_info = NetworkInfo(self.interface, self.ignore_interfaces, self.detached_down, self.unknown_up)
# Don't require importing basiciw unless using the functionality it offers.
if any(s in self.format_up or s in self.format_up for s in
['essid', 'freq', 'quality', 'quality_bar']):
get_wifi_info = True
else:
get_wifi_info = False
self.network_info = NetworkInfo(self.interface, self.ignore_interfaces, self.detached_down, self.unknown_up,
get_wifi_info)
# Don't require importing psutil unless using the functionality it offers.
if any(s in self.format_up or s in self.format_down for s in
['bytes_sent', 'bytes_recv', 'packets_sent', 'packets_recv', 'network_graph']):
self.network_traffic = NetworkTraffic(self.unknown_up, self.divisor, self.round_size)
else:
self.network_traffic = None
self.colors = self.get_hex_color_range(self.start_color, self.end_color, int(self.upper_limit))
self.kbs_arr = [0.0] * self.graph_width
def cycle_interface(self):
def cycle_interface(self, increment=1):
interfaces = [i for i in netifaces.interfaces() if i not in self.ignore_interfaces]
if self.interface in interfaces:
next_index = (interfaces.index(self.interface) + 1) % len(interfaces)
next_index = (interfaces.index(self.interface) + increment) % len(interfaces)
self.interface = interfaces[next_index]
elif len(interfaces) > 0:
self.interface = interfaces[0]
self.network_traffic.clear_counters()
self.kbs_arr = [0.0] * self.graph_width
if self.network_traffic:
self.network_traffic.clear_counters()
self.kbs_arr = [0.0] * self.graph_width
def get_network_graph(self, kbs):
# Cycle array by inserting at the start and chopping off the last element
@ -311,28 +335,27 @@ class Network(IntervalModule, ColorRangeModule):
format_values = dict(kbs="", network_graph="", bytes_sent="", bytes_recv="", packets_sent="", packets_recv="",
interface="", v4="", v4mask="", v4cidr="", v6="", v6mask="", v6cidr="", mac="",
essid="", freq="", quality="", quality_bar="")
color = None
if self.network_traffic:
network_usage = self.network_traffic.get_usage(self.interface)
format_values.update(network_usage)
if self.graph_type == 'input':
kbs = network_usage['bytes_recv']
elif self.graph_type == 'output':
kbs = network_usage['bytes_sent']
else:
raise Exception("graph_type must be either 'input' or 'output'!")
network_usage = self.network_traffic.get_usage(self.interface)
format_values.update(network_usage)
format_values['network_graph'] = self.get_network_graph(kbs)
format_values['kbs'] = "{0:.1f}".format(round(kbs, 2)).rjust(6)
color = self.get_gradient(kbs, self.colors, self.upper_limit)
network_info = self.network_info.get_info(self.interface)
format_values.update(network_info)
if self.graph_type == 'input':
kbs = network_usage['bytes_recv']
elif self.graph_type == 'output':
kbs = network_usage['bytes_sent']
else:
raise Exception("graph_type must be either 'input' or 'output'!")
format_values['network_graph'] = self.get_network_graph(kbs)
format_values['kbs'] = "{0:.1f}".format(round(kbs, 2)).rjust(6)
format_values['interface'] = self.interface
if sysfs_interface_up(self.interface, self.unknown_up):
if self.dynamic_color:
color = self.get_gradient(kbs, self.colors, self.upper_limit)
else:
if not self.dynamic_color:
color = self.color_up
self.output = {

View File

@ -62,7 +62,6 @@ class Reddit(IntervalModule):
}
on_leftclick = "open_permalink"
on_click = "open_link"
_permalink = ""
_url = ""
@ -136,6 +135,9 @@ class Reddit(IntervalModule):
"color": color,
}
def open_mail(self):
user_open('https://www.reddit.com/message/unread/')
def open_permalink(self):
user_open(self._permalink)

View File

@ -0,0 +1,107 @@
from i3pystatus import IntervalModule
import requests
from collections import OrderedDict
from bs4 import BeautifulSoup
class WhosOnLocation():
email = None
password = None
session = None
def __init__(self, email, password):
self.email = email
self.password = password
self.session = requests.Session()
def login(self):
login_details = {'email_input': self.email,
'password_input': self.password,
'_redirect_url': '',
'continue_submit': 'Login'}
r = self.session.post('https://login.whosonlocation.com/login', data=login_details)
return r.url == 'https://au.whosonlocation.com/home?justloggedin=true'
def get_status(self):
r = self.session.get('https://au.whosonlocation.com/home?justloggedin=true')
html = BeautifulSoup(r.content)
status = html.body.find("span", {"class": "my-status-name"})
if status:
return status.text
def on_site(self):
return self.__change_status('onsite')
def off_site(self):
return self.__change_status('offsite')
def __change_status(self, status):
r = self.session.post('https://au.whosonlocation.com/ajax/changestatus', data={'status': status})
return r.json()
# _type can be org or location
def search(self, keyword, _type='location'):
payload = {'keyword': keyword, 'type': _type}
r = self.session.get('https://au.whosonlocation.com/home/search', params=payload)
return self.__parse_results(BeautifulSoup(r.content))
@staticmethod
def __parse_results(page):
titles = ['Name', 'Title', 'Department', 'Current Location', 'Home Location']
table = page.body.find_all("tr", {"class": "dataRow"})
results = []
for row in table:
values = [v.string for v in row.findAll('td', {'class': 'truncate'})]
results.append(OrderedDict(zip(titles, values)))
return results
class WOL(IntervalModule):
"""
Change your whosonlocation.com status.
Requires the PyPi module `beautifulsoup4`
"""
location = None
email = None
password = None
settings = (
'email',
'password'
)
color_on_site = '#00FF00'
color_off_site = '#ff0000'
format = 'Status: {status}'
status = None
on_leftclick = 'change_status'
def init(self):
self.location = WhosOnLocation(self.email, self.password)
if not self.location.login():
raise Exception("Failed to login")
def change_status(self):
if self.status == 'On-Site':
self.location.off_site()
elif self.status == 'Off-Site':
self.location.on_site()
def run(self):
self.status = self.location.get_status()
color = None
if self.status == 'Off-Site':
color = self.color_off_site
elif self.status == 'On-Site':
color = self.color_on_site
self.output = {
"full_text": self.format.format(
status=self.status
),
"color": color
}