Skip to content

Instantly share code, notes, and snippets.

@cocuh
Last active April 30, 2016 15:17
Show Gist options
  • Save cocuh/cf2c5bc4716b92a14e290acd47edbfbe to your computer and use it in GitHub Desktop.
Save cocuh/cf2c5bc4716b92a14e290acd47edbfbe to your computer and use it in GitHub Desktop.
import os
import sys
if sys.version_info.major == 2:
import Tkinter as tk
else:
import tkinter as tk
abstractmethod = lambda f: f
class IRoll:
@abstractmethod
def get_name(self, student_id):
"""return name if the id is exists. If not, return None.
:type student_id: str
:rtype: str
"""
pass
@abstractmethod
def get_affiliation(self, student_id):
"""return affiliation if the id is exists. If not, return None.
:type student_id: str
:rtype: str
"""
pass
@abstractmethod
def get_match_ids(self, part_ids):
"""
return id with partial match. if no match, return None.
:type part_ids: [str]
:rtype: [str]
"""
pass
@abstractmethod
def get_ids(self):
"""
return all ids
:rtype: [str]
"""
pass
class TwinsRoll(IRoll):
CSV_ID_IDX = 3
CSV_NAME_IDX = 4
CSV_AFFILIATION_IDX = 1
CSV_REQ_COLUMN_SIZE = 5
def __init__(self, path):
self.path = path
self.database = self._parse_csv(path)
def get_name(self, student_id):
return self.get_attr(self.CSV_NAME_IDX, student_id)
def get_affiliation(self, student_id):
return self.get_attr(self.CSV_AFFILIATION_IDX, student_id)
def get_attr(self, column_idx, student_id):
for line in self.database:
if line[self.CSV_ID_IDX] == student_id:
return line[column_idx]
def get_match_ids(self, part_ids):
match_ids = []
for line in self.database:
for part in part_ids:
if part not in line[self.CSV_ID_IDX]:
break
else:
match_ids.append(line[self.CSV_ID_IDX])
return match_ids
def get_ids(self):
ids = []
for line in self.database:
student_id = line[self.CSV_ID_IDX]
if student_id:
ids.append(student_id)
return ids
@classmethod
def _parse_csv(cls, path):
data = open(path).read().splitlines()
res = []
for line in data:
record = [word.strip('"')for word in line.split(",")]
if len(record) < cls.CSV_REQ_COLUMN_SIZE:
continue
if not record[cls.CSV_ID_IDX].isdigit():
continue
res.append(record)
return res
class TwinsFormatter:
def format(self, roll, student_ids):
res = ['"No.","学籍番号","出席"']
for no, student_id in enumerate(roll.get_ids()):
line = '"{no}","{id}","{is_attend}"'.format(
no=no+1,
id=student_id,
is_attend='1' if student_id in student_ids else '',
)
res.append(line)
return '\n'.join(res)
class MainWindow(tk.Frame):
def __init__(self, master, roll, formatter, output_filename):
super(MainWindow, self).__init__(master)
self.roll = roll
self.pack()
self.init_widgets()
self.init_binds()
self.update_students_listbox()
self._keywords = []
self._focused_student_id = None
self.done_student_ids = set()
self.output_filename = output_filename
self.formatter = formatter
def init_widgets(self):
f_left = tk.Frame(self)
f_left.pack({'fill':tk.BOTH, 'side':'left'})
self.elem_listbox = tk.Listbox(f_left)
self.elem_listbox.pack({'fill': tk.BOTH, 'side':'top'})
self.elem_entry = tk.Entry(f_left, textvariable=self.gen_entry_callback())
self.elem_entry.pack({'side':'bottom'})
self.elem_entry.focus_set()
f_right = tk.Frame(self)
f_right.pack({'fill':tk.BOTH})
self.elem_donelistbox = tk.Listbox(f_right)
self.elem_donelistbox.pack({'side': 'right'})
def init_binds(self):
self.master.bind('<Escape>', self.action_destroy)
self.master.bind('<Control-s>', self.action_destroy)
self.master.bind('<Control-c>', self.action_destroy)
self.master.bind('<Control-q>', self.action_destroy)
self.master.bind('<Return>', self.action_append)
self.master.bind('<KP_Enter>', self.action_append)
self.master.bind('<Tab>', self.action_completion)
def action_destroy(self, *args):
self.save()
self.master.destroy()
def action_append(self, *args):
if self._focused_student_id is None:
return
self.done_student_ids.add(self._focused_student_id)
self._focused_student_id = None
self.update_done_listbox()
self.elem_entry.delete(0, tk.END)
def action_completion(self, *args):
if len(self._keywords) == 0:
student_ids = self.roll.get_ids()
else:
student_ids = self.roll.get_match_ids(self._keywords)
if len(student_ids) == 1:
self.elem_entry.delete(0, tk.END)
self.elem_entry.insert(0, student_ids[0])
return 'break'
def gen_entry_callback(self):
def callback(sv):
keywords = []
for oneword in [w for w in sv.get().split(' ')if w]:
for kw in oneword.split('.'):
if kw:
keywords.append(kw)
self._keywords = keywords
student_ids = self.roll.get_match_ids(self._keywords)
self.update_students_listbox(student_ids)
if len(student_ids) == 1:
self._focused_student_id = student_ids[0]
string_var = tk.StringVar()
string_var.trace('w', lambda name, idx, mode, sv=string_var:callback(sv))
return string_var
def update_students_listbox(self, student_ids=None):
if student_ids is None:
student_ids = self.roll.get_ids()
self.elem_listbox.delete(0, tk.END)
for student_id in student_ids:
record = "{id} {name}".format(
id=student_id,
name=self.roll.get_name(student_id),
)
self.elem_listbox.insert(tk.END, record)
def update_done_listbox(self):
self.elem_donelistbox.delete(0,tk.END)
student_ids = sorted(self.done_student_ids,key=lambda k:self.roll.get_ids().index(k))
for student_id in student_ids:
record = "{id} {name}".format(
id=student_id,
name=self.roll.get_name(student_id),
)
self.elem_donelistbox.insert(tk.END, record)
def save(self):
text = self.formatter.format(self.roll, self.done_student_ids)
print(text)
with open(self.output_filename, 'w') as fp:
fp.write(text)
def usage():
print("{cmd} meibo.csv output.csv".format(cmd=sys.argv[0]))
def main():
if len(sys.argv) != 3:
usage()
sys.exit(1)
meibo_filename = sys.argv[1]
output_filename = sys.argv[2]
roll = TwinsRoll(meibo_filename)
w = tk.Tk()
main_window = MainWindow(w, roll, TwinsFormatter(), output_filename)
main_window.mainloop()
if __name__ == '__main__':
main()
We can make this file beautiful and searchable if this error is corrected: It looks like row 2 should actually have 2 columns, instead of 3 in line 1.
"ninja年度 夏学期",
"ZZ16180","虚数学群","ペネストレーション実験I"
"John Doe",
"No.","学生所属","年次","学籍番号","氏名","氏名カナ",,,,,,,,,,,,,,,,"履修科目番号","備考"
"1","虚数学群忍者学類","1","204511333","John Does","ジョン ドウズ"
"2","虚数学群忍者学類","1","204511334","Arch Linux","サイコウ オーエス"
"3","虚数学群忍者学類","1","204511335","Wiki Pedia","ウィキ ペディア"
"4","虚数学群忍者学類","1","204511404","ぬくぬく おふとん","キエル ゴゼン"
"5","虚数学群忍者学類","1","204511435","名無 権兵衛","ナナシ ゴンベイ"
"6","虚数学群忍者学類","2","204411333","張三李四","チョウサンリシ"
"7","非実在性学群美少女学類","2","204411604","(๑╹◡╹๑)","にこにこ"
No. 学籍番号 出席
1 204511333
2 204511334 1
3 204511335 1
4 204511404 1
5 204511435
6 204411333
7 204411604
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment