Guest User

Untitled

a guest
Apr 24th, 2018
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.48 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # ================================================ General Info ========================================================
  5. """
  6. Course: Visualization and Sonification
  7. EX1
  8. Name: Omer Arie Lerinman
  9. ID: 304919863
  10. """
  11.  
  12. # ===================================================== Imports ========================================================
  13.  
  14. # General
  15. from datetime import datetime
  16. from collections import defaultdict
  17. from datetime import date, timedelta
  18. import matplotlib.pyplot as plt
  19. import math
  20. from copy import deepcopy
  21. import os
  22.  
  23. # GUI
  24. from tkinter import *
  25. from matplotlib.offsetbox import OffsetImage, AnnotationBbox
  26. from matplotlib.widgets import Button
  27. import numpy as np
  28. from scipy.misc import imread
  29. import matplotlib.cbook as cbook
  30.  
  31. # SOUND
  32. from pydub import AudioSegment
  33. import pyaudio
  34. from pydub.playback import *
  35.  
  36. try:
  37. import thread
  38. except ImportError:
  39. import _thread as thread
  40. # ===================================================== Constants ======================================================
  41.  
  42. DB_PATH = 'all_data_db.txt'
  43. CODING = 'UTF-16'
  44. DATE_TIME_PATTERN = '%Y-%m-%d %H:%M:%S'
  45. example_line = '2017-11-26 13:36:00, 864, 754, 941, 955, 814, 751, 839, 899'
  46. SEP = os.path.sep
  47. IMAGES_PATH = os.path.abspath('.') + SEP + 'images'
  48. images_path = IMAGES_PATH + SEP + '{}.png'
  49. KNESSET_PATH = IMAGES_PATH + SEP + 'knesset.png'
  50. SOUND_PATH = 'voices' + SEP + 'edited' + SEP
  51. HATIKVA_VOICES_PATHS = SOUND_PATH + '{}.wav'
  52. HATIKVA_PLAYBACK_PATH = SOUND_PATH + 'playback.wav'
  53. SEPARATOR = ';'
  54. FIELDS = ['en_name', 'id', 'expenses',
  55. 'presence_days', 'he_name']
  56. DB_FORMAT_LINE = 'format:\t' + SEPARATOR.join(FIELDS) + '\n'
  57. FORMAT_LINE = SEPARATOR.join(['{' + field + '}' for field in FIELDS]) + '\n'
  58. TITLE = "Visualization & Sonification \ Omer Arie Lerinman"
  59.  
  60.  
  61. # ===================================================== MK class ======================================================
  62.  
  63. class MK:
  64. def __init__(self, he_name='_', en_name='_', mk_id=0, expenses=0, presence_days=None, social=None):
  65. if not presence_days:
  66. self.presence_days = timedelta()
  67. else:
  68. self.presence_days = presence_days
  69. self.he_name = he_name
  70. self.en_name = en_name
  71. self.expenses = expenses
  72. self.social = social
  73. self.id = mk_id
  74. if mk_id:
  75. self.image_path = images_path.format(mk_id)
  76. self.voice_path = HATIKVA_VOICES_PATHS.format(mk_id)
  77. else:
  78. self.image_path = '_'
  79. self.voice_path = '_'
  80.  
  81. def has_values(self):
  82. return self.id != 0 and self.presence_days.seconds > 0 and self.social
  83.  
  84.  
  85. # ===================================================== Global variables
  86.  
  87. # Data
  88. mk_dict = defaultdict(MK)
  89.  
  90. # Gui
  91. BACKGROUND_IMAGE_PATH = os.path.abspath('images\\knesset.png')
  92. mk_indices_in_gui = {}
  93.  
  94. # Sound
  95. thread_indices = np.arange(0, 8)
  96. tikva_paths = [HATIKVA_VOICES_PATHS.format('p' + str(i)) for i in thread_indices]
  97. anthem_records = [AudioSegment.from_file(path, format="wav") for path in tikva_paths]
  98. playback_tikva = AudioSegment.from_file(HATIKVA_PLAYBACK_PATH, format="wav")
  99. duration_in_milliseconds = len(playback_tikva)
  100.  
  101.  
  102. # ===================================================== DB utils ======================================================
  103.  
  104.  
  105. def load_known():
  106. global mk_dict
  107. mk_dict[936] = MK(en_name='Betsalel Smutrich', mk_id=936, social=-22)
  108. mk_dict[878] = MK(en_name='Yair Lapid', mk_id=878, social=40)
  109. mk_dict[924] = MK(en_name='Oren Hazan', mk_id=924, social=-50)
  110. mk_dict[903] = MK(en_name='Stav Shafir', mk_id=903, social=63)
  111. mk_dict[871] = MK(en_name='Tamar Zandberg', mk_id=871, social=43)
  112. mk_dict[881] = MK(en_name='Meirav Michaeli', mk_id=881, social=56)
  113. mk_dict[208] = MK(en_name='Ahmed Tibi', mk_id=208, social=23)
  114. mk_dict[900] = MK(en_name='Ofer Shelach', mk_id=900, social=63)
  115. mk_dict[846] = MK(en_name='Hanin Zuabi', mk_id=846, social=18)
  116. mk_dict[955] = MK(en_name='Jehooda Glik', mk_id=955, social=-13)
  117. mk_dict[938] = MK(en_name='Iman Udah', mk_id=938, social=40)
  118.  
  119.  
  120. def write_db():
  121. global mk_dict
  122. with open(DB_PATH, 'w', encoding=CODING) as f:
  123. f.write(DB_FORMAT_LINE)
  124. for mk in mk_dict.values():
  125. f.write(FORMAT_LINE.format(he_name=mk.he_name, en_name=mk.en_name, id=mk.id,
  126. expenses=mk.expenses, presence_days=mk.presence_days.seconds / 3600))
  127.  
  128.  
  129. def load_db():
  130. global mk_dict
  131. with open(DB_PATH, 'r', encoding=CODING) as f:
  132. first_line = True
  133. for line in f:
  134. if first_line:
  135. first_line = False
  136. continue
  137. mk_as_array = line.split(SEPARATOR)
  138.  
  139. en_name = mk_as_array[0]
  140. mk_id = int(mk_as_array[1])
  141. expenses = int(mk_as_array[2])
  142. presence_days = timedelta(seconds=int(float(mk_as_array[3]) * 3600))
  143. he_name = mk_as_array[4]
  144. if mk_id > 0:
  145. mk_dict[mk_id] = MK(he_name=he_name, en_name=en_name, mk_id=mk_id, expenses=expenses,
  146. presence_days=presence_days)
  147. else:
  148. mk_dict[en_name] = MK(he_name=he_name, en_name=en_name, mk_id=mk_id, expenses=expenses,
  149. presence_days=presence_days)
  150.  
  151.  
  152. # ===================================================== get data ======================================================
  153.  
  154. def get_expenses():
  155. """
  156. get the expenses of the MK's
  157. :return:
  158. """
  159. with open('public_expenses_2017.txt', 'r', encoding=CODING) as f:
  160. for line in f:
  161. words = line.split(' ', 1)
  162. words[-1] = words[-1][:-1]
  163. if words[-1] == '':
  164. words.pop()
  165. expense = int(float(words[-1]))
  166. name = ' '.join(words[:-1])
  167. in_dict = False
  168. for id, mk in mk_dict:
  169. if name == mk.he_name:
  170. in_dict = True
  171. mk.expenses = expense
  172. if not in_dict:
  173. mk_dict[name] = MK(he_name=name, expenses=expense)
  174.  
  175.  
  176. def get_presence():
  177. global mk_dict
  178. total_counted_days = 0
  179. with open('presence_2017.txt') as f:
  180. prev_day = -1
  181. prev_presence_mk = []
  182. day_presence_dict = defaultdict(dict)
  183. prev_date_time = None
  184. continue_to_next_day = False
  185.  
  186. def add_to_mk(mk_id):
  187. if day_presence_dict[mk_id]:
  188. if mk_id not in mk_dict:
  189. mk_dict[mk_id] = MK(mk_id=mk_id)
  190. if 'in' not in day_presence_dict[mk_id] or 'out' not in day_presence_dict[mk_id]:
  191. k = 1 # todo delete
  192. mk_dict[mk_id].presence_days += day_presence_dict[mk_id]['out'] - day_presence_dict[
  193. mk_id]['in']
  194.  
  195. for line in f:
  196. # Parse
  197. words = line.split(', ')
  198. words[-1] = words[-1][:-1]
  199. if words[-1] == '':
  200. words.pop()
  201. date_time_str = words[0]
  202. cur_date_time = datetime.strptime(date_time_str, DATE_TIME_PATTERN)
  203.  
  204. # if cur_date_time.minute == 25 and cur_date_time.hour == 11 and cur_date_time.day==12: # todo delete
  205. # k=1
  206. # init time variables
  207. year = cur_date_time.year
  208. date_object = date(year=year, month=cur_date_time.month, day=cur_date_time.day)
  209. weekday = date_object.weekday() # Return the day of the week as an integer, where Monday is 0 and Sunday 6
  210.  
  211. if prev_day != weekday:
  212. continue_to_next_day = False
  213. prev_day = weekday
  214. prev_presence_mk = []
  215. prev_date_time = cur_date_time # empty dict for a new day
  216. if day_presence_dict:
  217. for mk_id in day_presence_dict:
  218. add_to_mk(mk_id)
  219. total_counted_days += 1
  220. day_presence_dict = defaultdict(dict)
  221. elif continue_to_next_day:
  222. continue
  223. if len(words) == 1 and not day_presence_dict:
  224. prev_date_time = cur_date_time
  225. continue # no MK's present
  226.  
  227. # Don't count days where the clock didn't operate for more then a hour
  228. if prev_date_time:
  229. gap = cur_date_time - prev_date_time
  230. if gap.seconds > 7200:
  231. day_presence_dict.clear()
  232. day_presence_dict = defaultdict(dict)
  233. prev_date_time = cur_date_time
  234. continue_to_next_day = True
  235. continue
  236.  
  237. # MK's are obligated to be present at monday and wednesday (Plenum day, aka Melia'a)
  238. if weekday in [0, 2]:
  239. prev_date_time = cur_date_time # empty dict for a new day
  240. continue
  241.  
  242. # Update current day's presence of each mk
  243. presence_mks = [int(mk) for mk in words[1:]]
  244. for mk_id in presence_mks:
  245. day_presence_dict[mk_id]['out'] = deepcopy(cur_date_time)
  246. if mk_id not in prev_presence_mk:
  247. day_presence_dict[mk_id]['in'] = deepcopy(cur_date_time)
  248.  
  249. # Calculate time of mk's who left
  250. mk_who_left = set(prev_presence_mk[:]) - frozenset(presence_mks[:])
  251. for mk_id in mk_who_left:
  252. add_to_mk(mk_id)
  253. del day_presence_dict[mk_id]
  254.  
  255. prev_presence_mk = presence_mks
  256. prev_date_time = cur_date_time
  257. # Normalize mk's presence in the number of counted days:
  258. for mk_id in mk_dict:
  259. mk_dict[mk_id].presence_days /= total_counted_days
  260.  
  261.  
  262. def init_indices():
  263. global mk_dict, mk_indices_in_gui
  264. for i, mk_id in enumerate(mk_dict):
  265. mk = mk_dict[mk_id]
  266. if mk.has_values():
  267. xy = (mk.social, mk.presence_days.seconds / 3600)
  268. mk_indices_in_gui[mk_id] = xy
  269.  
  270.  
  271. knesset_img = None
  272.  
  273.  
  274. def init_gui2():
  275. global mk_indices_in_gui, mk_dict, knesset_img
  276. datafile = cbook.get_sample_data(KNESSET_PATH)
  277. fig, ax = plt.subplots()
  278. plt.tight_layout()
  279. plt.title(TITLE)
  280. plt.xlabel('Social rank')
  281. plt.ylabel('Average presence in the Knesset')
  282. ax.set_xlim(-70, 85)
  283. ax.set_ylim(0, 12)
  284. plt.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9)
  285. # cid2 = fig.canvas.mpl_connect('motion_notify_event', motion_notify_callback) # todo unhide
  286. img = imread(datafile)
  287. plt.subplots_adjust(bottom=0.2)
  288. callback = PlayButtons()
  289. axstart = plt.axes([0.1, 0.05, 0.1, 0.075])
  290. bnext = Button(axstart, 'Start')
  291. bnext.on_clicked(callback.start)
  292. plt.imshow(img, zorder=0)
  293. plt.show()
  294.  
  295.  
  296. def gui_2_helper(mk_id=None):
  297. global mk_indices_in_gui, image
  298. fig, ax = plt.subplots()
  299. plt.tight_layout()
  300. plt.title(TITLE)
  301. plt.xlabel('Social rank')
  302. plt.ylabel('Average presence in the Knesset')
  303. ax.set_xlim(-70, 85)
  304. ax.set_ylim(0, 12)
  305. plt.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9)
  306. if mk_id:
  307. mk = mk_dict[mk_id]
  308. arr_img = plt.imread(mk.image_path, format='png')
  309. imagebox = OffsetImage(arr_img, zoom=0.2)
  310. imagebox.image.axes = ax
  311. xy = mk_indices_in_gui[mk_id]
  312. image_box = AnnotationBbox(imagebox, xy, xybox=(0, 0), xycoords='data', boxcoords="offset points",
  313. box_alignment=(-0.5, -0.5), pad=0, arrowprops=dict(arrowstyle="->"))
  314. ax.add_artist(image_box)
  315. plt.show(block=False)
  316.  
  317.  
  318. # ===================================== init threads
  319.  
  320. chapters = None
  321. playback_slices = None
  322.  
  323.  
  324. def init_sound():
  325. global chapters, playback_slices, anthem_records, playback_tikva, duration_in_milliseconds, mk_indices_in_gui
  326. p = pyaudio.PyAudio()
  327. a = p.get_default_output_device_info()
  328. print('Sound device:\t' + str(a['name']))
  329. number_of_voices = len(tikva_records)
  330. number_of_mk_to_present = len(mk_indices_in_gui)
  331. number_of_slices = number_of_mk_to_present + 2
  332. chapters_length = math.floor(duration_in_milliseconds / number_of_slices)
  333. playback_slices = list(playback_tikva[::chapters_length])
  334. tikva_records_slices = [list(version[::chapters_length]) for version in tikva_records]
  335. mk_in_gui = list(mk_indices_in_gui.keys())
  336. for mk_id, i in zip(mk_in_gui, np.arange(1, number_of_slices)):
  337. mk = mk_dict[mk_id]
  338. # sociality is between -24 to 75
  339. cur_number_of_voices = math.floor(((mk.social + 24) / 99) * number_of_voices)
  340. for j in range(cur_number_of_voices):
  341. playback_slices[i] = playback_slices[i].overlay(tikva_records_slices[j][i])
  342. gain = min(mk.presence_days.seconds / 3600, 12) / 12 # between [0,1]
  343. gain_change = float(-(1 - gain)) * 10
  344. playback_slices[i].apply_gain(gain_change)
  345. chapters = [None] + mk_in_gui + [None]
  346.  
  347.  
  348. def play_sonification():
  349. global chapters, playback_slices
  350. for i, mk_id in enumerate(chapters):
  351. p = playback_slices[i]
  352. plt.close()
  353. gui_2_helper(mk_id)
  354. play(p)
  355.  
  356.  
  357. # ===================================================== buttons # ======================================================
  358.  
  359. class PlayButtons(object):
  360. def start(self, event):
  361. play_sonification()
  362.  
  363.  
  364. # ===================================================== Main ======================================================
  365.  
  366. if __name__ == '__main__':
  367. load_known()
  368. # load_db()
  369. # get_expenses()
  370. get_presence()
  371. init_indices()
  372. # write_db()
  373. init_sound()
  374. while 1:
  375. init_gui2()
Add Comment
Please, Sign In to add comment