rescuetime_linux_uploader_25/0000755000175000017500000000000010745211252015656 5ustar angusangusrescuetime_linux_uploader_25/README0000644000175000017500000000104010745211252016531 0ustar angusangusUsage: =============== Install the python X library (http://python-xlib.sourceforge.net or python-xlib package under Ubuntu) Open the firefox_extension/rescuetime_exporter.xpi file in Firefox, and follow the instructions within Firefox to install it. Run the uploader/uploader.py script, and follow the instructions to enter your username and password. These will be stored in $HOME/.rescuetime/preferences, should you need to change them. Add the uploader script to your Gnome or KDE session, if you want it to start each time you log in. rescuetime_linux_uploader_25/firefox_extension/0000755000175000017500000000000010745211252021414 5ustar angusangusrescuetime_linux_uploader_25/firefox_extension/rescuetime_exporter.xpi0000644000175000017500000000502310745211252026233 0ustar angusangusPK/8P@. install.rdfUT N\RTPK݅/8j,chrome.manifestUT 7GGUx10k<FLJC0 Ύ%bG0&Zj]lM7O !5a>u"rF4N* ]}eE/Ӯ}PK}584chrome/rescuetime_exporter.jarUT GGUx ffa```PZeޗ: +bQ<ԢJNg%V0`~,zʐ?<)16E{,M=4+(O`uqRYgiK= ȝk+^yj۶;.Mou}ChMHpзxw'A Լ̢Դ +JsN^bӋA }agq!{kHEA,=yq/ߗTe2M5zpr/dޔ#ݮ<%y\ kG۶5-9*'͹!Ge}?0p] ~,xێk.;ILV|azɫ_0Y]!z~f߳fL6ťc[" :6_+rf_ٙT ;r4z/Ú;\砣JMʖE&-9X])]-ʻɴ"uĹ2&w|.@A6[Ws9gi\jm߶I*]ۛc+,}&=Asܦ+_[aViY҈h]Vm~fWki`Ų30!E,eARk>z;KPv׭\Ļ4/#sVKgMǛsve-uҕfN՗|[<ޭyFή-n5G*S׸sV6㧨qRoܒye/UPHŵᓅød=x%grͺ9QšqBjGjc6V,as0K i+[0q4-} v+IRc\aOavn3k6=[a17'\_+_٧tf6϶c:O/&h> e˒K?N-]U2+t/^v綎߉M9Y̷9{lώ^y\SY]tw!4ݼޅ<]{a_ɯu~{ݻγ9 q7RW4vC]=濑?Ap*\3uw͚yK({(9u_^Q1vrVJ-V|Qt2>s\{a)m^cjO<4\yqOMta:Vi *^-ĕ}N^d~h#d?2|3[Y|KVgs+hHAbO!/9I9^?Y񦟿LZʱ'r&hlF;P ʺMO/ot~'fj_G ;vY%+N+r񮚱^Nٿ^iK[ĤsK/tbOxvZs*u$xV,d֓7?{i=SJ욞a fdgU2dba笐<(2٨@_ )q , M`bZBal @H`PK/8P@. install.rdfUT in their titles. So this is a hack to try and fix this. """ return data.replace("&", "&").replace(">", ">").replace("<", "<") class time_obj(object): """ A structure that holds information about a slice of time, and the window/app that was focused at that time. """ def __init__(self, options): """ Creates a new object, initializing its start_time, app_name, and window_title properties based on the current time and focused application. """ self.options = options self.start_time = datetime.datetime.today() self.app_name = self.get_active_application() self.window_title = self.get_active_window_title() if self.window_title == "": if self.options.debug: print "Window title is blank.", display.get_input_focus().focus.get_wm_class() self.window_title = "Untitled window" self.end_time = None extended_info = self.get_extended_info() print self.app_name, "(" + str(self.window_title) + ")", "now focused", "(Extended info: " + str(extended_info) + ")" if extended_info != None: self.extended_info = extended_info def get_extended_info(self): """ Tries to get any extended info available for the application stored in self.app_name. Returns None if there is none available for that application. """ if self.app_name == None: return None extension_name = os.path.join( "extensions", self.app_name, "extended_info") try: os.stat(extension_name) except OSError: return None if self.options.debug: print "Getting extended info from", extension_name execfile(extension_name) return locals()["extension_get_extended_info"](self) def get_active_application(self): """ Returns the application name of the focused application, such as Firefox or Gnome-terminal. Sometimes returns None. """ if hasattr(self.options, "force_active_application"): return self.options.force_active_application try: """ # Old code, does not work without compiz if display.get_input_focus().focus == None or display.get_input_focus().focus.get_wm_class() == None: print "No active application" return None app_name = display.get_input_focus().focus.get_wm_class()[1] """ exec_cmd = "bash get_active_window.sh" fin, fout = os.popen4(exec_cmd) total_str = fout.read() if self.options.debug: print "total_str:", total_str try: app_name = eval("[" + total_str + "]")[1] except SyntaxError: return None # Extensions can override the application name extension_name = os.path.join( "extensions", app_name, "app_name") try: os.stat(extension_name) except OSError: return unicode(app_name).encode('utf8') if self.options.debug: print "Getting application name from", print extension_name execfile(extension_name) app_name = locals()["extension_get_active_application"](self, app_name) return unicode(app_name).encode('utf8') except: raise return None def still_active(self): """ Returns True if the application that is active is the same as the one that was active when this object was first created. Checks the application name and the window title. """ if self.app_name != self.get_active_application(): return False if self.window_title != self.get_active_window_title(): return False if hasattr(self, 'extended_info'): if self.extended_info != self.get_extended_info(): return False return True def close(self): """ Assigns this object's end_time parameter to the current time. """ self.end_time = datetime.datetime.today() def get_active_window_title(self): """ Returns the window title of the focused application, or None sometimes. """ try: exec_cmd = "bash get_active_window.sh" fin, fout = os.popen4(exec_cmd) total_str = fout.read() app_name = eval("[" + total_str + "]")[2][2:] return app_name #return unicode(display.get_input_focus().focus.get_wm_name()).encode('utf8') except: return None def print_out(self,id): """ Prints out an xml version of this object, suitable for uploading to rescuetime.com The id should be an integer that is different for each object printed out. """ if self.window_title == None or self.window_title.isspace() or len(self.window_title) == 0: return "" if self.app_name == None or self.app_name.isspace() or len(self.app_name) == 0: return "" out = '\n' % id out += ' ' + escape_xml(str(self.start_time).replace("\..*", "")) + '\n' out += ' ' + escape_xml(str(self.app_name)) + "\n" out += ' ' + escape_xml(str(self.window_title)) + '\n' out = out + ' ' + escape_xml(str(self.end_time)) + '\n' out += ' ' + escape_xml(os_name) + '\n' if hasattr(self, "extended_info"): out += '' + escape_xml(self.extended_info) + '' out += '\n' return out def handle_signal(a , b): """ -- Currently not working, needs to be redone. A function suitable for acting as a signal handler. Prepares the latest data for uploading, then quits. Does NOT upload anything. """ pass class Uploader(object): def __init__(self, options): self.options = options self.dir_structure = DirStructure(self.options) f = file(self.dir_structure.preference_file) data = f.readlines() f.close() self.scan_interval_seconds = 5 self.upload_interval_minutes = 30 email_address = False password = False for line in data: LL = line.split(":") if LL[0].strip() == "scan interval": self.scan_interval_seconds = int(LL[1].strip()) if LL[0].strip() == "upload interval": self.upload_interval_minutes = int(LL[1].strip()) if LL[0].strip() == "email": email_address = LL[1].strip() if LL[0].strip() == "password": password = LL[1].strip() if not email_address or not password: while True: email_address = raw_input("Your rescuetime username: ") password = getpass.getpass("Your rescuetime password: ") if not self.test_login(email_address, password): print "Login failed." else: break # Dump out the newly added email and password f = file(self.dir_structure.preference_file, "w") f.write("email: " + email_address + "\n") f.write("password: " + password + "\n") f.write("scan interval: " + str(self.scan_interval_seconds) + "\n") f.write("upload interval: " + str(self.upload_interval_minutes) + "\n") f.close() self.upload_interval = datetime.timedelta(0, self.upload_interval_minutes*60) self.time_objs = [] f = file(self.dir_structure.next_upload_file) self.next_upload = int(f.readline()) f.close() def shutdown(self): try: os.unlink(self.dir_structure.lock_file) except OSError: pass def main(self): """ Goes into a loop, monitering the status of the focused window, and periodically uploading all the data it gains. """ try: self.time_objs = [] self.time_objs.append(time_obj(self.options)) next_upload_time = datetime.datetime.today() + self.upload_interval try: while True: # If the focused window has changed if not self.time_objs[len(self.time_objs) - 1].still_active(): self.time_objs[len(self.time_objs) - 1].close() self.time_objs.append(time_obj(self.options)) # Check if it has been 30 minutes yet if datetime.datetime.today() > next_upload_time: self.prepare_data() self.upload_data() self.time_objs = [] self.time_objs.append(time_obj(self.options)) next_upload_time = datetime.datetime.today() + self.upload_interval # Sleep for a few seconds time.sleep(self.scan_interval_seconds) # Aborted by keyboard except KeyboardInterrupt: self.prepare_data() if "y" == raw_input("Upload the latest data? y/n: ").lower()[0]: self.upload_data() finally: self.shutdown() def test_login(self, email_address, password): print "Logging in..." login_data = urllib.urlencode([("email", email_address),("password", password)]) login_url = "http://www.rescuetime.com/api/handshake" try: ret = urllib2.urlopen(login_url, login_data) except urllib2.HTTPError: return False login_status = "".join(ret.readlines()) print login_status return "login success!" in login_status.lower() def upload_data(self): """ Attempts to upload all the data files stored in PREPARED_DIR. Moves any that are successful to FINISHED_DIR, and any that fail to FAILED_DIR. """ print "Submitting data..." f = file(self.dir_structure.preference_file) pref_data = f.readlines() f.close() for line in pref_data: LL = line.split(":") if LL[0].strip() == "password": password = LL[1].strip() if LL[0].strip() == "email": email_address = LL[1].strip() if not self.test_login(email_address, password): print "It looks like the login failed." print "Please edit your password and username in", self.dir_structure.preference_file return print "Uploading data..." for prepared_file in os.listdir(self.dir_structure.prepared_dir): finished_name = os.path.join(self.dir_structure.finished_dir, prepared_file) old_name = os.path.join(self.dir_structure.prepared_dir, prepared_file) failed_name = os.path.join(self.dir_structure.failed_dir, prepared_file) print "Uploading", prepared_file, "..." submit_url = "http://www.rescuetime.com/api/userlogs" f = file(old_name) submit_data = urllib.urlencode([("email", email_address), ("password", password), ("xmldata", "".join(f.readlines()))]) f.close() try: ret = urllib2.urlopen(submit_url, submit_data) except urllib2.HTTPError: print "Error uploading." continue returned_data = "".join(ret.readlines()) print returned_data f = file(os.path.join(self.dir_structure.reply_dir, prepared_file), "w") f.write(returned_data) f.close() if not "error" in returned_data: os.rename(old_name, finished_name) else: os.rename(old_name, failed_name) # This is pointless """ print "Getting new file..." core_data_url = "http://www.rescuetime.com/xsd/CoreData.xsd" ret = urllib2.urlopen(core_data_url) newest_file = "".join(ret.readlines()) try: os.stat(self.dir_structure.xsd_file_storage_path) except OSError: f = file(self.dir_structure.xsd_file_storage_path, "w") f.write(newest_file) f.close() f = file(self.dir_structure.xsd_file_storage_path) older_file = "".join(f.readlines()) f.close() if older_file != newest_file: print "WARNING: Mismatch between latest xsd file and older one." """ print "Done submitting data." def prepare_data(self): """ Prepares the given data to be uploaded, and stores the prepared data in PREPARED_DIR. Does not upload the data. """ self.time_objs[len(self.time_objs) - 1].close() i = 0 data = '\n' for to in self.time_objs: data += to.print_out(i) i += 1 data += "" # Save the uploaded data f = file(os.path.join(self.dir_structure.prepared_dir, str(self.next_upload) + ".upload.encoded.data"), "w") f.write(data) f.close() self.next_upload += 1 f = file(self.dir_structure.next_upload_file, "w") f.write(str(self.next_upload)) f.close() if __name__ == "__main__": # Set up signal handlers signal.signal(signal.SIGTERM, handle_signal) signal.signal(signal.SIGHUP, handle_signal) print "Process:", os.getpid() uploader = Uploader(Options()) uploader.main()