Last active
April 30, 2016 15:17
-
-
Save cocuh/cf2c5bc4716b92a14e290acd47edbfbe to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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","(๑╹◡╹๑)","にこにこ" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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