forked from roxana-lafuente/ResearchLogger
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ResearchLoggerInterface.py
279 lines (251 loc) · 10.4 KB
/
ResearchLoggerInterface.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
# !/usr/bin/env python
# -*- coding: utf-8 -*-
##############################################################################
#
# ResearchLogger: Python Keylogger scientific purposes in Linux and Windows
# Copyright (C) 2015 Roxana Lafuente <[email protected]>
#
# 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/>.
#
##############################################################################
from Tkinter import *
import tkSimpleDialog
from constants import EXTENSION, WINDOWTITLE, BASE_PATH
import os.path
import codecs
import sys
import time
import os
import string
import argparse
class TranslationWindow(Frame):
"""
This is the window which has the translation source text and target
text. It initializes the keylogging activity.
The onset is the time when the test start. For us, the onset is the
time when the researcher chooses a file that the subjects should
translate. The file selection can be done in two different ways:
- By double clicking on the chosen file.
- By selecting the file with one click and then pressing enter.
We start the keylogging activity before showing the file
chooser window so that the time when the file is chosen is logged.
The logging activity finishes when the translation window is closed.
This is logged by our program and then it stops logging data. There is
only one way to close a translation window and that is by clicking once
in the X button on the right upper corner of the screen.
"""
def __init__(self, parent, path):
Frame.__init__(self, parent)
self.parent = parent
self.path = path
# Gets the subject name.
self.username = self.get_username()
# Gets the source text.
self.filename = self.get_filename()
# Starts the User Interface.
# For the experiments with translation memories, we don't need a
# graphical interface.
self.init_ui()
# Updates the source tex.
self.set_filename(self.filename)
def get_username(self):
"""
Gets the subject ID.
"""
# Hides the main window. (This is necessary to ask for the subject id)
self.parent.withdraw()
# Asks for the subject id.
username = None
exists = False
username = tkSimpleDialog.askstring("Enter Subject ID", "Please "
"insert you subject id here.")
# Always in lowercase to avoid conflicts between Linux and Windows.
if username is not None:
username = string.lower(username)
exists = self.check_log_existance(username)
if username is None or username == '' or exists is True:
print "Error. Username cannot be empty."
sys.exit(1)
# Shows the main window that had been hidden.
self.parent.deiconify()
return username
def check_log_existance(self, username):
"""
Checks whether a log file exists. If it does, it returns True.
Otherwise, it returns
False.
A log file is of the form "logs-"+username.
"""
logdir = "logs-" + username
return logdir in os.listdir(os.getcwd())
def get_filename(self):
"""
Selects a file using tkFileDialog.
"""
import tkFileDialog
# Dialog window to select a file.
ftypes = [('All files', '*')]
dlg = tkFileDialog.Open(self, filetypes=ftypes)
filename = dlg.show()
return filename
def init_ui(self):
"""
Initiates the User Interface.
"""
self.start_time = time.strftime("%Y-%m-%d %H:%M:%S")
self.parent.title(WINDOWTITLE)
# Overrides the default behavious of the close button.
self.parent.protocol("WM_DELETE_WINDOW", self.on_exit)
# Menu Bar.
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
# Source Text.
self.sourcetext = Text(self.parent, width=110, height=16,
font="Verdana 12", wrap="word", bd=0,
bg="white smoke", state=DISABLED)
self.sourcetext.pack(fill=BOTH, expand=1, padx=10, pady=5)
self.sourcetext.tag_configure('title', font=('Verdana', 12),
underline=1, justify='center')
self.sourcetext.tag_configure('text', font=('Verdana', 12))
# Target Text Box.
self.targettext = Text(self.parent, width=110, height=10,
state='normal', font="Verdana 12")
self.targettext.pack(fill=BOTH, expand=1, padx=10, pady=5)
def set_filename(self, filename):
"""
Tries to open a file that contains the source code.
"""
# The textbox needs to be able to be modified.
self.sourcetext.config(state=NORMAL)
try:
# Tries to open the file.
f = open(filename, 'r')
except IOError as ioe:
# The file couldn't be opened because it doesn't exist!
msg = "Error de I/O al abrir el archivo que contiene el texto "
msg += "fuente. Error {0}: {1}."
print msg.format(ioe.errno, ioe.strerror)
self.on_exit()
sys.exit(1) # Abnormal termination.
except:
msg = "Error fatal al abrir el archivo que contiene el texto "
msg += "fuente:"
print msg, sys.exc_info()[0]
sys.exit(1) # Abnormal termination.
else:
# Cleans the textbox.
self.sourcetext.delete(0.0, END)
# Inserts the the title of the source text.
title = f.readline()
self.sourcetext.insert(END, title, 'title')
# Inserts the body of the source text.
txt = f.read()
self.sourcetext.insert(END, txt, 'text')
# Closes the file.
f.close()
# Now the text has been loaded but it shouldn't be modified by the
# user.
self.sourcetext.config(state=DISABLED)
def save_default(self, fname, tstmp):
"""
Saves the translation on a default dir.
"""
msg = "Guardando el archivo en el directorio por defecto."
print msg
filename = BASE_PATH + '/' + self.username + '_' + fname + '_' + tstmp + EXTENSION
self.f = codecs.open(filename, mode='w+', encoding="utf-8")
def on_save(self):
"""
Saves the content of the targettext field into a file named
user_text.txt.
"""
# Tries to get the name that will be created.
# Creation of this file may fail in case nothing has been translated,
# So we do nothing since there's nothing to be saved.
# Timestamp to make sure that we save different versions of a
# translation done by the same subject.
tstmp = str(int(time.time()))
fname = self.filename.split("/")[-1]
fname = fname.split('.')[0]
filename = self.path + self.username + '_' + fname + '_' + tstmp + EXTENSION
print "Guardando archivo:", filename
# Gets the target text and set the start and end timestamps.
self.end_time = time.strftime("%Y-%m-%d %H:%M:%S")
ttext = "Start time: %s\nEnd time: %s\n" % (self.start_time, self.end_time)
ttext += "Final document:\n" + self.targettext.get(0.0, END)
# Creates the file where the target text will be saved.
try:
directory = filename.split('/')
directory.pop()
directory = "/".join(directory)
directory += '/'
if not os.path.isdir(directory):
os.mkdir(directory)
self.f = codecs.open(filename, mode='w+', encoding="utf-8")
except IOError:
# Tries to create a file that already exists.
msg = "Error fatal al guardar el texto meta:"
print msg, sys.exc_info()[0]
self.on_exit(False)
self.save_default(fname, tstmp)
sys.exit(1) # Abnormal termination.
except OSError:
msg = "Imposible crear el directorio especificado.\n"
print msg
self.save_default(fname, tstmp)
try:
# Save the translated text in a file.
self.f.write(ttext)
except UnicodeEncodeError, reason:
print ttext
print reason
sys.exit(1)
self.f.close()
def on_exit(self, saves=True):
"""
Saves the target text before exiting the translation interface.
"""
assert(isinstance(saves, bool))
if saves:
self.on_save()
self.parent.destroy()
def parse():
"""
Returns the command line option for the log directory.
"""
parser = argparse.ArgumentParser(description='Options for Keylogger.')
parser.add_argument('-d', default=BASE_PATH,
help='Directory to save the log files.')
args = parser.parse_args()
dir = args.d
# Dir need to end with '/'
if not dir.endswith('/'):
dir += '/'
return dir
def main():
# Parses command line options.
path = parse()
# Creates the main window.
root = Tk()
# Prepares the translation window.
ex = TranslationWindow(root, path)
RWidth = root.winfo_screenwidth()
RHeight = root.winfo_screenheight()
root.minsize(1000, 450)
root.maxsize(1200, 650)
root.geometry((("%dx%d") % (RWidth, RHeight)) + '+20+20')
# Begins our program.
root.mainloop()
if __name__ == '__main__':
main()