snose

Check-in [5d0589b3be]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge in Python3 stuff
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | master
Files: files | file ages | folders
SHA3-256: 5d0589b3beec72b26fb301adee4d3dbdbcf7391ac533a382db30fde0da38bfeb
User & Date: simon 2018-10-11 07:54:42
Context
2018-10-11
10:25
Add ability to determine filename from first few lines of a file

Wanted to do this for awhile, but was too lazy until now.

Most of my snose files have the filepath as the first or second line in the
file (in a comment). This makes it easier to sneeze files out. check-in: ba8e456fb6 user: simon tags: master, trunk

07:54
Merge in Python3 stuff check-in: 5d0589b3be user: simon tags: master, trunk
2018-10-06
17:56
Python3 compatibility - iteritems vs items

I think that might be all that's needed. This was never super duper advanced
code. Leaf check-in: 7dedaed4cc user: atomicules tags: python3

16:28
Simperium compatibility

Use version instead of syncnum (since I _think_) with Simperium version does
what both of these did: I.e. version will increment whether content or metadata
changes.

Some import tweaks.

(Gah! This code is horrible, but still works) check-in: f66d33d3c7 user: atomicules tags: master, trunk

Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to snose.py.

1
2
3

4
5
6
7
8
9
10
#!/usr/bin/python
#snose - Simplenote Object Synchronisation (Explicit)


import json
import simplenote #Need to install this
import os.path, time
from optparse import OptionParser
import netrc

def main():



>







1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/python
#snose - Simplenote Object Synchronisation (Explicit)

import sys
import json
import simplenote #Need to install this
import os.path, time
from optparse import OptionParser
import netrc

def main():
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159




160

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233




234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

	if not options.username or not options.password:
		#Check to see if stored somewhere
		try:
			options.username = netrc.netrc().authenticators("simple-note.appspot.com")[0]
			options.password = netrc.netrc().authenticators("simple-note.appspot.com")[2]
		except IOError as e:
			print 'Username and password must be supplied or exist .netrc file under domain of simple-note.appspot.com'
			exit()
	snclient = simplenote.Simplenote(options.username, options.password)
	if options.snort:
		snort(snclient, options.snort)
	elif options.sniff:
		sniff(snclient, options.sniff[0], options.sniff[1])
	elif options.sneeze:
		sneeze(snclient, options.sneeze[0], options.sneeze[1])
	elif options.blow:
		blow(snclient, options.blow)
	elif options.snot:
		snot(snclient)
	elif options.sync and options.hanky:
		sync(snclient, True)
	elif options.sync:
		sync(snclient)
	else:
		print 'No options supplied'


def snort(snclient,filename): 
	# Add a new mapping, is actually add a new file
	try: #http://stackoverflow.com/questions/82831/how-do-i-check-if-a-file-exists-using-python#85237
		with open('.snose', 'r') as f:
			snose = json.load(f)
	except IOError as e:
		#Doesn't exist so create new
		snose = {}
	#Add new file to Simplenote
	#Need to get file contents
	try:
		with open(filename, 'r') as f:
			content = f.read()
	except IOError as e:
		print "Failed to read file %s" % filename
	else:
		try:
			returned = snclient.add_note({"content": content, "tags": ["snose"]})
			print "Imported %s into Simplenote with key %s" % (filename, returned[0]['key'])
		except IOError as e:
			print "Failed to add note to Simplenote"
			print e
		else:
			#Add mapping
			snose[filename] = {'key': returned[0]['key'], 'version': returned[0]['version'], 'modifydate': float(os.path.getmtime(filename)) } #Use actual file mod date
			try:
				#Write back out
				with open('.snose', 'w') as f:
					json.dump(snose, f, indent=2)
			except IOError as e:
				print "Failed to update .snose index file"
				#But note was added to Simplenote so?
				print "But note was successfully imported to Simplenote with key %s. Try sniffing the file" % returned[0]['key']


def sniff(snclient,key, filename): #How to ensure remote gets or has snose tag?
	# Add a new mapping only
	try:
		with open('.snose', 'r') as f:
			snose = json.load(f)
	except IOError as e:
		#Assuming doesn't exist so create new
		snose = {}
	#Get details about current Simplenote file
	try:
		remote = snclient.get_note(key)
		#What if can't be found, need to abort...
	except IOError as e:
		print "Failed to find that note on Simplenote"
		print e
	else:
		try:
			#Add mapping
			snose[filename] = {'key': remote[0]['key'], 'version': remote[0]['version'], 'modifydate': float(os.path.getmtime(filename)) }
			#Write back out
			with open('.snose', 'w') as f:
				json.dump(snose, f, indent=2)
		except IOError as e:
			print "Failed to update .snose index file"
			#Don't need to do anything else)


def sneeze(snclient, key, filename):
	#place an existing note in current directory
	try:
		with open('.snose', 'r') as f:
			snose = json.load(f)
	except IOError as e:
		#Doesn't exist so create new
		snose = {}
	#Get remote note
	try:
		remote = snclient.get_note(key)	
	except IOerror as e:
		print "Failed to find that note on Simplenote"
		print e
	else:
		#Write file
		try: 
			with open(filename, 'w') as f:
				f.write(remote[0]['content'])
		except IOError as e:
			print "Failed to create local copy of that note"
			print e
		else:
			#Update index
			try:
				snose[filename] = {'key': remote[0]['key'], 'version': remote[0]['version'], 'modifydate': float(os.path.getmtime(filename)) } #Need to set as local modified date as otherwise will want to sync it straight away.
				#Write back out
				with open('.snose', 'w') as f:
					json.dump(snose, f, indent=2)
			except IOError as e:
				 print "Failed to update .snose index file"
				 print "But note was created locally. Try sniffing the file to add it to the index."


def blow(snclient, key):
	#With given key from .snose file, roll back to the previous version

	#1) Check exists in .snose index
	#2) Get previous version of remote
	#3) Write it out locally
	#4) Use that to update remote
	#5) Update index file with results

	#1) Check exists in .snose index
	try:
		with open('.snose', 'r') as f:
			snose = json.load(f)
		#Need to get filename of note, loop through, performance should be fine as .snose likely to be small




		filename = [name for name, local in snose.iteritems() if local['key'] == key][0]

		print "Attempting to rollback file %s" % filename
	except IOError as e:
		print "Note doesn't exist in local .snose index"
	else:
		#2) Get previous version of remote
		try:
			#fetch once to know version
			remote = snclient.get_note(key)
			rollback = snclient.get_note(key, remote[0]['version']-1)
		except IOError as e:
			print "Failed to fetch previous version"
		else:
			try: 
				#3) Write it out locally
				with open(filename, 'w') as f:
					f.write(rollback[0]['content'])
				print "Rolled back local copy"
			except IOError as e:
				print "Failed to rollback local copy of that note"
				print e
			else:
				#Since rollback doesn't include full meta data, update remote accordingly
				try:
					del remote[0]['version']
				except KeyError:
					pass
				finally:
					#Set modified date
					sysmodifydate = float(os.path.getmtime(filename))
					remote[0]['modifydate'] = sysmodifydate
					remote[0]['content'] = rollback[0]['content']
					#4) Use that to update remote
					try:
						returned = snclient.update_note(remote[0])
						print "Rolled back remote version"
					except IOError as e:
						print "Failed to roll back remote version"
					else:
						#Get returned metadata
						snose[filename]['version'] = returned[0]['version']
						snose[filename]['modifydate'] = sysmodifydate
						try:
							#5) Update index file with results
							with open('.snose', 'w') as f:
								json.dump(snose, f, indent=2)
						except IOError as e:
							 print "Failed to update .snose index file"
							 print "Try running a sync to get index corrected."


def snot(snclient):
	#List simplenote notes tagged with "snose"
	notelist = snclient.get_note_list()
	#That gets list of keys. Then need to iterate through and get first line of text.
	#This is going to be slow.
	print "Key:                               \tNote"
	for note in notelist[0]:
		if ("snose" in note['tags']) & (int(note['deleted']) != 1):
			#get note itself
			remote = snclient.get_note(note['key'])
			print remote[0]['key']  + " \t" + remote[0]['content'].splitlines()[0]


def sync(snclient, dry=False):
	#Need to read in mappings and sync those notes.
	dryremotes = []
	try:
		with open('.snose', 'r') as f:
			snose = json.load(f)
	except IOError as e:
		print 'Error reading Index file'
	else:
		#Need to iterate through list.




		for name, local in snose.iteritems():
			#First of all check for local modifications
			sysmodifydate = float(os.path.getmtime(name))
			if sysmodifydate > float(local['modifydate']): #ensure full timestamp
				if not dry:
				#Update remote
					try:
						with open(name, 'r') as f:
							content = f.read()
					except IOError as e:
						print "Failed to read local note %s" % name
						print "Skipping synchronisation for this note"
					else: 
						try:
							returned = snclient.update_note({'key': local['key'], 'version': local['version'], 'content': content, 'modifydate': sysmodifydate })
							print "Updated remote version of %s" % name
						except IOError as e:
							print "Failed to update remote verison of local note %s" % name
						else:
							#Get returned metadata
							snose[name]['version'] = returned[0]['version']
							snose[name]['modifydate'] = sysmodifydate #Use local value to avoid differences in accuracy (decimal places. etc) between local and remote timestamps
							#Update local file if merged content
							if 'content' in returned[0]:
								try:
									with open(name, 'w') as f:
										f.write(returned[0]['content'])
									print "Merged local content for %s" % name
									#Override the returned value? As otherwise next sync will immediately update the remote version for no reason.
									snose[name]['modifydate'] = os.path.getmtime(name) 
								except IOError as e:
									print "Failed to merge content locally for %s" % name
									print "Therefore skipping updating the index for this note" #I think this is a good idea?
							#Update the index file
							try:
								with open('.snose', 'w') as f:
									json.dump(snose, f, indent=2)
							except IOError as e:
								print "Failed to update index for changes regarding local file %s" % name
								print "But remote and local copy of the file itself have been updated."
								#What now? I don't know.
				elif dry:
					print "Updated remote version of %s" % name
					#For dry run, collect list of "updated remotes" to ignore in local updates
					dryremotes.append(name)
			#Fetch details from Simplenote
			try:
				remote = snclient.get_note(local['key'])
			except IOError as e:
				print "Failed to fetch remote copy of note %s" % name
				print "Skipping synchronisation for this file"
			else:
				if remote[0]['version'] > local['version']:
					if not dry:
						try: 
							with open(name, 'w') as f:
								f.write(remote[0]['content'])
							print "Updated local version of %s" % name
						except IOError as e:
							print "Failed to update local note %s with remote content" % name
							print "Will not updatet the .snose index file for this file"
						else:
							#Also update .snose index
							snose[name]['version'] = remote[0]['version']
							snose[name]['modifydate'] = os.path.getmtime(name) #As if set remote modify date, local file will immediately appear 'modified'
							try:
								with open('.snose', 'w') as f:
									json.dump(snose, f, indent=2)
							except IOError as e:
								print "Failed to update index"
								print "But local copy of the file %s has been updated with remote changes" % name
							#Some feedback
					elif (dry and (not (name in dryremotes))):
						print "Updated local version of %s" % name


main()







|

















|
















|



|

|
|








|

|















|
|








|















|
|


|



|
|








|
|
















>
>
>
>
|
>
|

|







|





|

|
|














|

|









|
|







|




|









|


>
>
>
>
|









|
|
|


|

|









|



|
|





|
|


|






|
|






|

|
|








|
|


|



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320

	if not options.username or not options.password:
		#Check to see if stored somewhere
		try:
			options.username = netrc.netrc().authenticators("simple-note.appspot.com")[0]
			options.password = netrc.netrc().authenticators("simple-note.appspot.com")[2]
		except IOError as e:
			print('Username and password must be supplied or exist .netrc file under domain of simple-note.appspot.com')
			exit()
	snclient = simplenote.Simplenote(options.username, options.password)
	if options.snort:
		snort(snclient, options.snort)
	elif options.sniff:
		sniff(snclient, options.sniff[0], options.sniff[1])
	elif options.sneeze:
		sneeze(snclient, options.sneeze[0], options.sneeze[1])
	elif options.blow:
		blow(snclient, options.blow)
	elif options.snot:
		snot(snclient)
	elif options.sync and options.hanky:
		sync(snclient, True)
	elif options.sync:
		sync(snclient)
	else:
		print('No options supplied')


def snort(snclient,filename): 
	# Add a new mapping, is actually add a new file
	try: #http://stackoverflow.com/questions/82831/how-do-i-check-if-a-file-exists-using-python#85237
		with open('.snose', 'r') as f:
			snose = json.load(f)
	except IOError as e:
		#Doesn't exist so create new
		snose = {}
	#Add new file to Simplenote
	#Need to get file contents
	try:
		with open(filename, 'r') as f:
			content = f.read()
	except IOError as e:
		print("Failed to read file %s" % filename)
	else:
		try:
			returned = snclient.add_note({"content": content, "tags": ["snose"]})
			print("Imported %s into Simplenote with key %s" % (filename, returned[0]['key']))
		except IOError as e:
			print("Failed to add note to Simplenote")
			print(e)
		else:
			#Add mapping
			snose[filename] = {'key': returned[0]['key'], 'version': returned[0]['version'], 'modifydate': float(os.path.getmtime(filename)) } #Use actual file mod date
			try:
				#Write back out
				with open('.snose', 'w') as f:
					json.dump(snose, f, indent=2)
			except IOError as e:
				print("Failed to update .snose index file")
				#But note was added to Simplenote so?
				print("But note was successfully imported to Simplenote with key %s. Try sniffing the file" % returned[0]['key'])


def sniff(snclient,key, filename): #How to ensure remote gets or has snose tag?
	# Add a new mapping only
	try:
		with open('.snose', 'r') as f:
			snose = json.load(f)
	except IOError as e:
		#Assuming doesn't exist so create new
		snose = {}
	#Get details about current Simplenote file
	try:
		remote = snclient.get_note(key)
		#What if can't be found, need to abort...
	except IOError as e:
		print("Failed to find that note on Simplenote")
		print(e)
	else:
		try:
			#Add mapping
			snose[filename] = {'key': remote[0]['key'], 'version': remote[0]['version'], 'modifydate': float(os.path.getmtime(filename)) }
			#Write back out
			with open('.snose', 'w') as f:
				json.dump(snose, f, indent=2)
		except IOError as e:
			print("Failed to update .snose index file")
			#Don't need to do anything else)


def sneeze(snclient, key, filename):
	#place an existing note in current directory
	try:
		with open('.snose', 'r') as f:
			snose = json.load(f)
	except IOError as e:
		#Doesn't exist so create new
		snose = {}
	#Get remote note
	try:
		remote = snclient.get_note(key)	
	except IOerror as e:
		print("Failed to find that note on Simplenote")
		print(e)
	else:
		#Write file
		try:
			with open(filename, 'w') as f:
				f.write(remote[0]['content'])
		except IOError as e:
			print("Failed to create local copy of that note")
			print(e)
		else:
			#Update index
			try:
				snose[filename] = {'key': remote[0]['key'], 'version': remote[0]['version'], 'modifydate': float(os.path.getmtime(filename)) } #Need to set as local modified date as otherwise will want to sync it straight away.
				#Write back out
				with open('.snose', 'w') as f:
					json.dump(snose, f, indent=2)
			except IOError as e:
				print("Failed to update .snose index file")
				print("But note was created locally. Try sniffing the file to add it to the index.")


def blow(snclient, key):
	#With given key from .snose file, roll back to the previous version

	#1) Check exists in .snose index
	#2) Get previous version of remote
	#3) Write it out locally
	#4) Use that to update remote
	#5) Update index file with results

	#1) Check exists in .snose index
	try:
		with open('.snose', 'r') as f:
			snose = json.load(f)
		#Need to get filename of note, loop through, performance should be fine as .snose likely to be small
		if sys.version_info < (3, 0):
			sitems = snose.iteritems()
		else:
			sitems = snose.items()
		filename = [name for name, local in sitems if local['key'] == key][0]

		print("Attempting to rollback file %s" % filename)
	except IOError as e:
		print("Note doesn't exist in local .snose index")
	else:
		#2) Get previous version of remote
		try:
			#fetch once to know version
			remote = snclient.get_note(key)
			rollback = snclient.get_note(key, remote[0]['version']-1)
		except IOError as e:
			print("Failed to fetch previous version")
		else:
			try: 
				#3) Write it out locally
				with open(filename, 'w') as f:
					f.write(rollback[0]['content'])
				print("Rolled back local copy")
			except IOError as e:
				print("Failed to rollback local copy of that note")
				print(e)
			else:
				#Since rollback doesn't include full meta data, update remote accordingly
				try:
					del remote[0]['version']
				except KeyError:
					pass
				finally:
					#Set modified date
					sysmodifydate = float(os.path.getmtime(filename))
					remote[0]['modifydate'] = sysmodifydate
					remote[0]['content'] = rollback[0]['content']
					#4) Use that to update remote
					try:
						returned = snclient.update_note(remote[0])
						print("Rolled back remote version")
					except IOError as e:
						print("Failed to roll back remote version")
					else:
						#Get returned metadata
						snose[filename]['version'] = returned[0]['version']
						snose[filename]['modifydate'] = sysmodifydate
						try:
							#5) Update index file with results
							with open('.snose', 'w') as f:
								json.dump(snose, f, indent=2)
						except IOError as e:
							 print("Failed to update .snose index file")
							 print("Try running a sync to get index corrected.")


def snot(snclient):
	#List simplenote notes tagged with "snose"
	notelist = snclient.get_note_list()
	#That gets list of keys. Then need to iterate through and get first line of text.
	#This is going to be slow.
	print("Key:                               \tNote")
	for note in notelist[0]:
		if ("snose" in note['tags']) & (int(note['deleted']) != 1):
			#get note itself
			remote = snclient.get_note(note['key'])
			print(remote[0]['key']  + " \t" + remote[0]['content'].splitlines()[0])


def sync(snclient, dry=False):
	#Need to read in mappings and sync those notes.
	dryremotes = []
	try:
		with open('.snose', 'r') as f:
			snose = json.load(f)
	except IOError as e:
		print('Error reading Index file')
	else:
		#Need to iterate through list.
		if sys.version_info < (3, 0):
			sitems = snose.iteritems()
		else:
			sitems = snose.items()
		for name, local in sitems:
			#First of all check for local modifications
			sysmodifydate = float(os.path.getmtime(name))
			if sysmodifydate > float(local['modifydate']): #ensure full timestamp
				if not dry:
				#Update remote
					try:
						with open(name, 'r') as f:
							content = f.read()
					except IOError as e:
						print("Failed to read local note %s" % name)
						print("Skipping synchronisation for this note")
					else:
						try:
							returned = snclient.update_note({'key': local['key'], 'version': local['version'], 'content': content, 'modifydate': sysmodifydate })
							print("Updated remote version of %s" % name)
						except IOError as e:
							print("Failed to update remote verison of local note %s" % name)
						else:
							#Get returned metadata
							snose[name]['version'] = returned[0]['version']
							snose[name]['modifydate'] = sysmodifydate #Use local value to avoid differences in accuracy (decimal places. etc) between local and remote timestamps
							#Update local file if merged content
							if 'content' in returned[0]:
								try:
									with open(name, 'w') as f:
										f.write(returned[0]['content'])
									print("Merged local content for %s" % name)
									#Override the returned value? As otherwise next sync will immediately update the remote version for no reason.
									snose[name]['modifydate'] = os.path.getmtime(name) 
								except IOError as e:
									print("Failed to merge content locally for %s" % name)
									print("Therefore skipping updating the index for this note")#I think this is a good idea?
							#Update the index file
							try:
								with open('.snose', 'w') as f:
									json.dump(snose, f, indent=2)
							except IOError as e:
								print("Failed to update index for changes regarding local file %s" % name)
								print("But remote and local copy of the file itself have been updated.")
								#What now? I don't know.
				elif dry:
					print("Updated remote version of %s" % name)
					#For dry run, collect list of "updated remotes" to ignore in local updates
					dryremotes.append(name)
			#Fetch details from Simplenote
			try:
				remote = snclient.get_note(local['key'])
			except IOError as e:
				print("Failed to fetch remote copy of note %s" % name)
				print("Skipping synchronisation for this file")
			else:
				if remote[0]['version'] > local['version']:
					if not dry:
						try: 
							with open(name, 'w') as f:
								f.write(remote[0]['content'])
							print("Updated local version of %s" % name)
						except IOError as e:
							print("Failed to update local note %s with remote content" % name)
							print("Will not updatet the .snose index file for this file")
						else:
							#Also update .snose index
							snose[name]['version'] = remote[0]['version']
							snose[name]['modifydate'] = os.path.getmtime(name) #As if set remote modify date, local file will immediately appear 'modified'
							try:
								with open('.snose', 'w') as f:
									json.dump(snose, f, indent=2)
							except IOError as e:
								print("Failed to update index")
								print("But local copy of the file %s has been updated with remote changes" % name)
							#Some feedback
					elif (dry and (not (name in dryremotes))):
						print("Updated local version of %s" % name)


main()