| 1 | #!/usr/bin/env python |
|---|
| 2 | #-*- coding:utf-8 -*- |
|---|
| 3 | |
|---|
| 4 | import re, datetime |
|---|
| 5 | import xml.sax.saxutils |
|---|
| 6 | import unicodedata |
|---|
| 7 | |
|---|
| 8 | class Comment (object): |
|---|
| 9 | """ |
|---|
| 10 | スレッド上のコメントを管理するクラスです. |
|---|
| 11 | """ |
|---|
| 12 | |
|---|
| 13 | __delete_tag = re.compile(r"<.+?>") |
|---|
| 14 | __date_and_id = re.compile(r"(?P<date>.*) ID:(?P<id>\S*)") |
|---|
| 15 | __be = re.compile(r"BE:(?P<be>.*)") |
|---|
| 16 | __datetime = re.compile(r"(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})\(.*\) (?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})(\.(?P<csec>\d+)|)") |
|---|
| 17 | __urls = re.compile(r"(ttps?:\/\/[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+)") |
|---|
| 18 | __response = re.compile(r">>(\d{1,4}|>>[0-9]{1,4})") |
|---|
| 19 | |
|---|
| 20 | def __init__(self, thread, line, number): |
|---|
| 21 | """ |
|---|
| 22 | オブジェクトのコンストラクタです. |
|---|
| 23 | |
|---|
| 24 | thread : コメントが保存されている親スレッドのインスタンス |
|---|
| 25 | line : 初期化に用いられる、datファイルの一行. |
|---|
| 26 | この引数はunicode型でなければなりません. |
|---|
| 27 | number : スレッドからの、コメント位置. |
|---|
| 28 | """ |
|---|
| 29 | self.thread = thread |
|---|
| 30 | self.number = number |
|---|
| 31 | self.line = line |
|---|
| 32 | |
|---|
| 33 | self.__datetime_cache = None |
|---|
| 34 | self.__urls_cache = None |
|---|
| 35 | if type(line) == unicode: |
|---|
| 36 | columns = line.split("<>") |
|---|
| 37 | elif type(line) == str: |
|---|
| 38 | columns = unicode(line, "Shift-JIS", "replace").split("<>") |
|---|
| 39 | else: raise TypeError("the type of the argument 'line' is not unicode or str.") |
|---|
| 40 | if len(columns) < 5 : raise TypeError |
|---|
| 41 | self.__name = Comment.__delete_tag.sub("", columns[0]) |
|---|
| 42 | self.__mailaddr = columns[1] |
|---|
| 43 | raw_body = Comment.__delete_tag.sub("", columns[3].replace(" <br> ", "\n")) |
|---|
| 44 | self.__body = xml.sax.saxutils.unescape(raw_body)[1:-1] # 余分なスペースの削除 |
|---|
| 45 | di_result = Comment.__date_and_id.search(columns[2]) |
|---|
| 46 | if di_result: |
|---|
| 47 | self.__date = di_result.group("date") or "" |
|---|
| 48 | self.__id = di_result.group("id") or "" |
|---|
| 49 | be_result = Comment.__be.search(columns[2]) |
|---|
| 50 | self.__be = None |
|---|
| 51 | if be_result: self.__be = be_result.group("be") |
|---|
| 52 | |
|---|
| 53 | self.__responses_cache = None |
|---|
| 54 | |
|---|
| 55 | def getName(self): return self.__name |
|---|
| 56 | name = property(getName) |
|---|
| 57 | |
|---|
| 58 | def getMailAddr(self): return self.__mailaddr |
|---|
| 59 | mailaddr = property(getMailAddr) |
|---|
| 60 | |
|---|
| 61 | def getDate(self): return self.__date |
|---|
| 62 | date = property(getDate) |
|---|
| 63 | |
|---|
| 64 | def getBody(self): return self.__body |
|---|
| 65 | body = property(getBody) |
|---|
| 66 | |
|---|
| 67 | def getDatetime(self): |
|---|
| 68 | if self.__datetime_cache: return self.__datetime_cache |
|---|
| 69 | |
|---|
| 70 | result = Comment.__datetime.search(self.__date) |
|---|
| 71 | if result: |
|---|
| 72 | year = int(result.group("year")) |
|---|
| 73 | month = int(result.group("month")) |
|---|
| 74 | day = int(result.group("day")) |
|---|
| 75 | hour = int(result.group("hour")) |
|---|
| 76 | m = int(result.group("min")) |
|---|
| 77 | sec = int(result.group("sec")) |
|---|
| 78 | c = result.group("csec") |
|---|
| 79 | csec = c == None and 0 or int(c) |
|---|
| 80 | |
|---|
| 81 | d = datetime.datetime(year, month, day, hour, m, sec, csec*10000) |
|---|
| 82 | self.__datetime_cache = d |
|---|
| 83 | return d |
|---|
| 84 | else: return None |
|---|
| 85 | datetime = property(getDatetime) |
|---|
| 86 | |
|---|
| 87 | def getID(self): return self.__id |
|---|
| 88 | ID = property(getID) |
|---|
| 89 | |
|---|
| 90 | def getBe(self): return self.__be |
|---|
| 91 | be = property(getBe) |
|---|
| 92 | |
|---|
| 93 | def extractUrls(self): |
|---|
| 94 | """ |
|---|
| 95 | コメントの内容から、URLの一覧を抽出して返します. |
|---|
| 96 | """ |
|---|
| 97 | if self.__urls_cache: return self.__urls_cache |
|---|
| 98 | result = Comment.__urls.finditer(self.body) |
|---|
| 99 | l = ["".join( ("h", r.group(0)) ) for r in result] |
|---|
| 100 | self.__urls_cache = l |
|---|
| 101 | return l |
|---|
| 102 | urls = property(extractUrls) |
|---|
| 103 | |
|---|
| 104 | def extractResponses(self, returnType="str"): |
|---|
| 105 | """ |
|---|
| 106 | コメントの内容から、レスポンスの一覧を抽出して返します. |
|---|
| 107 | """ |
|---|
| 108 | if self.__responses_cache == None: |
|---|
| 109 | result = Comment.__response.finditer(self.body) |
|---|
| 110 | l = [r.group(0) for r in result] |
|---|
| 111 | self.__responses_cache = l |
|---|
| 112 | |
|---|
| 113 | l = self.__responses_cache |
|---|
| 114 | if returnType == "str": return l |
|---|
| 115 | elif returnType == "int": |
|---|
| 116 | ld = [int(unicodedata.normalize("NFKC", m[2:])) for m in l] |
|---|
| 117 | return ld |
|---|
| 118 | if returnType == "comment": |
|---|
| 119 | ld = [int(unicodedata.normalize("NFKC", m[2:])) for m in l] |
|---|
| 120 | lc = [self.thread[i] for i in ld if 0 < i <= self.thread.res] |
|---|
| 121 | return lc |
|---|
| 122 | else: raise TypeError |
|---|
| 123 | |
|---|
| 124 | res = property(extractResponses) |
|---|
| 125 | responses = property(extractResponses) |
|---|
| 126 | |
|---|
| 127 | def render(self): |
|---|
| 128 | """ |
|---|
| 129 | 取得したコメントから、整形された文章を返します. |
|---|
| 130 | """ |
|---|
| 131 | if self.be: |
|---|
| 132 | header = u"%i 名前:%s [%s]: %s ID:%s BE:%s" % \ |
|---|
| 133 | (self.number, self.name, self.mailaddr, self.date, self.ID, self.be) |
|---|
| 134 | else: |
|---|
| 135 | header = u"%i 名前:%s [%s]: %s ID:%s" % \ |
|---|
| 136 | (self.number, self.name, self.mailaddr, self.date, self.ID) |
|---|
| 137 | return u"%s\n%s\n" % (header, self.body) |
|---|
| 138 | |
|---|
| 139 | def __unicode__(self): return self.render() |
|---|