zb_\xfe\x80\xd1^B\x88\xfbul\xff\x9b{\x8e*\xe5\xa2\xd5J9e\xc1R\xbe\xd3U\xa6\xac\xb8\\)\x1bu\x95+:\xe6\x03\x14c\xd1\x11\x06\x86\x1el\x01\xd2\xcb\xc0O\x02\x02A\xd8\xa48\x06cc\x05\xa6a\x00\xcfC\x1b\xf1x\'[\xaf=L%v\x9dM\xf2\xdc\x04$\x7f\xc6A\xdd\xbf\xa8\xe5\xb3BT\xeb7\xfbW\xa6\x19\xee\x86\x8d\xf4h{\x97\xe3\x07\xc9`\xcf\x10\xa4ga\n\xb4C\x94\x89\x89{?\xf0\xd8\xb3\xb5\xa7\x0c\x11\x12\x18\x1b\xd3\xa6\xaf\x00-\xcc\xe6\xc6l\x89\xc1c>{\x11\xb8l\x9du\x11^\xe7\xcf\xebT_\x08\x85\xf1\xe2\xed\x1e\xa8\x02\xc3\xb5\xe3\xa9\xb7\xd6~\x12\x08~OC\x95\xd76gU\xb3j\xb1\xe67\xf5\xff\x90\x88\xf71\x91{u\xefP\x06\xff\x12\xa1K\xe0\xba\xdf \x1d\xf7\xe3\x9c\x84w^\xe3\xd1\xbb2\xc3[\x86\xda\\\x17\x92\xb3\x7f\xed=\xb0\xa7#A\x03\xf6\xb2\xd2!\x06\xb0y\xbc\xabl6\xce\x17\x1aY.5\xfbM\xbc\x1f\xb3\xfb\x0c\tl1\xfd\xdfF\x83V\x9c6^\xd20\xe7\xeb00/\xfeJ\xbf^\xa9\xacv\xf5\x9a\xe5\x80\xee\xe3\xcbGN\xe8\xa9\xf9\xf1\xb49\'\x0em\x8e\td\xf0m\xd4|\xb5\xd6\xd4#\x12\x0b\xc0\xc1\x03I\xdc\xf4\xe4\xd1e\xb9_/_>x|O\xf5\xf8/\xb1\xf3=5a\x88\xd3O=\xfa\xb5O\x9fR\xca\xde]5\x83\xef\x8e\xc7\x7fT\xcayo\xea \x81Ie\x8f\xceu\x08!\xceL\x1a\xc2y\xa7\xb5\xf8)=\xcd\x04\xe3\xb4\x7f\x8a\xb3\xd0\xef\xb4\x05S\xe8\x8d\xcd\x89U\xab@\x0c4<"M\xbe<[\x96\xf6B\xf4\x18&\x9b\x82(\x85\x00!+\x84\x08\x1d bHj\xb8\x1b6\x9c#\xf0\xdd\xef)\xdfnk\x0e\x02\xae\x1dy\xf0,\x98"\xad\xcd\xec\xb1=\'\x100Sr\xe53\x87 \xc4\x9a\xc2"\x82"\x9d\xa1|\x87\xa3\x0f3[\x1b\x0cg\x9d\xda\xfe\x06\xcf\xcf\xaeg\xcf\xbf|\xeb\x1b*\xb3R\x0c\xf13@T>\x07\xf2\x13\x1c\x985B\x01B\xb1\xde\x9c\xbf\xa0|\xa6,\xbb\x06\xfa\xccK\x01\x12\'\xa2\xb2d\xcfb\xafS.\x9c\x14\xf6\x81\x10\x80\xdf\xed\x7f\xca\xb0\x07\x1b\x817\x9a#\xeb\x9fz\x16\xeb\xeey\xddSZ\xbb\xd3\xd0\xccK\xca\xac\xed\x93JBW\xf2b\xe2\xbf\xc8p\x99\xccj\x9c\xde=P\\\xf6\xb2K6\x15;\x18\x1c\'!\x93\xe1\x94F\x8d6\x08\xf1\x82\xed\x18\x98\x80\xc0\x14\x08&\x12>\x9ddl\xa0.\xb1\x0b\x1e\xb7`\xb9\xbf\xfb\x90R\x8e\x99\xb3<\xb3b\x87Q\xbf\xd7U\x1ey\xf8\x1b\xd5Q\xb7w\xc7.&X\xfaBE7Tg\x19\x9f\x80\xbet|\x04\'t\xdf^\xaf\x81@\xf4\xf4O\xba)\xa4\xa05\xb5\xa9E\x9fy\xe83e\xbd;\xde[N\xeb~\xb8f\xdd1\x0fh\x14\x82b\xdf\xee\xa3\xca\x0fz\xbe]\x05AR]\x87z\x84YH\xd7xyWi\xfa\xb48&\x96q\xf6\xee#zj\x82\x8fx3\x98\x0f\x8e\xca8c\xe3\xf3X\xa7\xda\x0f\x11\x93\xd4q\xa6\t\x7f!\\D\xdc.R\x9a\x11G4K\xff\xf7\xda9\x05\t}\xba?\x0c#\x8a\x93voI\x07f\xc3\xfb_\x98\x80\xf0h\xa7\xca&\x84\xd6\x7f\x02\x11\x86M>F2\xf9R\xa7\x9fpl\xe2\xfeh\xc8gS\xe3\xe1\x95f%\xf6\xcf\xdaarB\x83\xc0\xc2\xbc\x04\t\xa1C\xe0\xa4\x1d\x1b\x7f\x92\xb0\x1f\xe1C\x99\xf1+\xf0U\xc5\xa9\xd8\xee\xa3\x08\x11%\xaf$\x11"\xcfB\xc8\xb5\x993\xb3\x103D\xd6{\x99\x0bh=G\x9a\xd9\x971v\xc9\xeaK\xf6\xadkD\x00\xa0\x1d\x02\x80\x10\nC\x13Nq<\xc6Ob\x9d\xf2\x0cy\r\xab\x96\xe1[=\xfb\x96\xcd\xef\xdf\xbcj\xb9\xe4C\xdb\x0c\x92\x9c\x96\xe6q\xd5\x18\x02\xf1s\xae\xd8\xd4\x84\xc3\xda\x8b\xea\xe6}_h\x87}\xff\xde\x9f\xf4T\r\xbd\xdb\x07\xdeS\xca\xfa]}!\xba\x13^^6\xb8w\xa3z>\xb9\xfa\x84\n\x0f\xbcx9_\xc2\'Oy\xb2|\xe5\xe2\xde\x1a\xfb\xa7=\xda%\xa26\x839\xe2>\x98* \xa30\x1b\x02\xb2 \x84\xc0Q\xdd\xd7\x95\xd5\x7f\xbfzY\xe4\xd4E\xaa`i\x17\x1e\r\xe7\x00\xff-\xbagBL\x84\x13!\xa0\xd1\xc7\xb1\xdd\xff(K\x9e\xbdd5K*\xac\x7f\xb6\xf7=\xc7\x9f"\x14\xce\'\xe8\x89\x10hKo\xfd\xfdh9\x0c5\x1cg\xd0\x8c8\x10n;\xb4Ix\x13\xb0\xd6\x12B\xb1\xf7\x04\xc0|[\xfeO\xdd\x87\xc1l\xcc\xf6\xe0O\x82e\xa0\xb4\xf1\x1c\tG\x86i"L\xfc\x1e4\x96Wr\xfe\x93\x99\xc7\xbeGw>\x87\xe9\xa1\x01\x02\x00\x1a\xb0\x96\x10\x00\xb3\x8c\xf0\x85<<\x0b%\xe5\xbbiz\xdb?\xee\xde\xf6\xfe{e\xac}2G\xdd\x1b&ez\xd0\xd01\x8b2\x885\xf3\x16F\xb2\xfe#u\xe4\xb5S~\x13\xbel\x87.\xd3\xc9hX\xe5\xc1\x9b<\xb4Iy\xdb5kU\x9b\x9f\xf4\xa4\x95%qHO\x15\xafek\x81\xd3\xbc\xae\xb4\x1d\xd8\xef\xc2IJHG\x19\x0b\x93\xce:4\xc8\xfc\xcd\x1fk\xb8\xef\xe4\xa7\x8e,\xcb\xfc|\x99\x9a\xf0#\xef^]\x00\xe6T*\xcaS\xbe\xe0\xf1\x0b\xd6<\x00\xde\xfc\xed\xce~\xaaf\x05\x9a\xe1G\x9b3\x118\xea\x92\x1c\x81\x19\x93\xbd\x17\xef0B\xb0!\xb2\xf8\xc4\xd89\xdd\xf6\xba\xa8\xb7\n\xa1\xe1\xc4us\xc4\xe9\x87`\x10\x90\xf3zf\xa9\xc1RX]\x03\x1a\xf0\xe2\xc7\xe8\x0b1\xdd[S\\%\x00\xbd|\xeb[*\x1aa2`v\xeb\x83\xa88\xb3\x10P\xdb\xf6\x9eYG\x86Xx\xd14\xe9.\x9c\xb4`\xc2\x8as\xcd\xbdr\xf0\xf2\xeb@`\x12|0R\xd0D\xff1p\xed\x1e\x04)\xdf\x9d\x16\xf1\xb5\xe7D\x10>\xd1\xb0I J\n7\xc6\xb0Vi\x18\xca\x0cMwe\x02\x8av\'d\x99\xacP\x15\xed\xef>\xfd/\xe6F\xd2f\xfd\xde\x1eM\x16-\x1e\xf8\x8d~|\xce\xf5\x93\x1c\x16d\x90\xb9\x18\x84H^i\x7f\x86\x81#\x80\xd2\xcd\t\x9d\x0ee?b"F\xb0\x0e\xb5\xe7`\xdbT\x89\x00k\x87.3\xd1hX\xf9\x01K\xff\xf2\xb5\xe5\xf0\x9e\x83\xcav\xdd\xbbV\x06\x7f\xcb\xb7\x1e\xab\xa1@p\xfc\x05\x1d\xed\xa6>^\xcb/\x8e=\r*e\\\xd1\xf2.\x8e\x90\xe2\xf9\xb5 \xb1\xa9c\x8fdj\xae\x8d\x031\xf9\th\xfdE\xb6\xfdG\xd5\x98+\xfej\xc5\xb2\xf5\xe3[\x96C\xba/\xa8\x9a\x1d\x1a\xd8\xec\x9e\xcd\xca\xbe={\x95\x83\xbb\xcf+K}\xf1\xa1\xf2\xa6\x0e#\xa5 \xc6\x82\xd3R\xb1\xa1\x93\x8dHR\xdbh\x84<\xdf\xc4?\xd7P#_\xc3H\x9c#\x16/\xce\x16\x02qBsy%>\x02\x07s\xb8\x06"[b\xe7{\xeb\x04_\xef\ts\xb1c\x992\xea\xe0U\xc0A)\xe2\xd8\xbe;\x12A4VG\x98\x96\xe6\xf5r\xefA"4\xa95\xf2\x0c\x84\x1c\x86&\xa8\x08\xbd\xbc0X\x90\x14\xa1\xd8\x1e\x896\x9a#\xf9\xedL\x0eL\xd6\x0eK\xa6\xb9\x88\x97\xb5}Msn5Q\xfd\x8e\xe9\xe5R\x10\xc4\x04\xb0\xb4b\x19\xac\xd6\x19\xd3cvN\xdb\xd8\xd6\xc9\x9dO\xb9{\x1b9`p\xfb\x99\xf6kI\x1c\xa3E\xddG\xd2\xc7cn\xc4D\xf2y4\x8f\xb6}?\x1d\xa3\t\x81\x91\xac\xc5p\x84\xc7@\xc7\xa8\x1c\x98j\xd8\xff\xda{F\x99\xfc\xf8Ve\xd3{6-\'v\xdfU\xc3y4\xbf\xd0\x9cx\xbd4N\xd9U\xaf\xe8h7u\xdd4sf\xd7\x89\xd5\x92\xd0\x08\xca\xa2\x83c\xf1\xdc\x06\x15\xc4<\xc0D\xa0\x9a\xd0\x8f\x02\x19\x1e\xffk\x9e>\xad\x96\xe5\x1e\xdas`Y\xf5/k\xd4P\xa0\xf6\xd2\x9b?\xfa\xc9\xf2\xb6\xdf\xbf\xad\x9a\x12\xa05TaC\xda#\xc0#\x00R\x9c\x81xm\x8c\xfbq\xad\xe1\x86\xd9\xd2\xb84\x9e|PR\xd8R\x8c\x9avI\x03U\xcf%j\xe2\xf9\x11\x92k#\x84y\'^]\x8b\x8c\x82B\xbe\xdb\xfd\xab\x8a\n\xc6{\xe2M\x7f\x04\xe0^\xdb\x897\xc9\xe9Hr\x94p\xa6\xc4\x9b\x0f5\xbb\xd5\xdf=s 2\x01\x90\xdc|\x0c\xea\xf7\x81\x12`\xa6w\xb4\xf3\xdb3\xbc\xd4\xfeb\xde\x84h\x93k\x0f\xf9\xa5I\xab{\xde\xa0\xd9\xaf\xd2\x84\x9e\x06\xea\x148\xf9\xec\x81gK\x1dK\x04@|\x04\xe8\x13\xf3:\xa7g\xf5\x1d\x89ALQ\xb4h\x9f\xd1fr\xedS\x81\xe8\x95\xfb 8\xa1#\xc9k\xfcT\x9c\x90\xe8\x92\x00\x18M\xd1\x96\xb5\x8d\xd0\xe9\x7f\xc4\x1f\x95\xaa\xbf\xb1\xca\xfe{\xce\x1c\xaa)\xbb\xd7}\xba\xa6\xe8\xfe\xfb\xde\xdd\xca\x86\xf7mT\xc7?\xf1\xba\xb3\x03-\x10\xa7\x9d\x84\x0e\x8d.@_\x7fc|H\x00#\xac\xd3\xf4u\xd9\x01!}\x07$CT\x16\xb2?$\xb4\xc1\x98\x96$_\xfa\x82eJ\xb9p\x95R\xf6\xed*\xe5]]\xe5\xfa\xa9\'\x94\x8f\xde\xf1\xd1R\xae\xdd\xbc\xdc\xfd\xd8\xf7\xcb\x05\xff<\xb3\xbc\xf6\xc2e\xab\xe3p\x8e\x1dz\x9e\x0b\xbd\xc5\xc9\x81\x88\x02%m@\xbb\xe1\x08\x89?\\\xa9\n>9gB~\x98\xa0\xa6"Oy\xa6\x12\x8cE\xf3\x7f\xf7\xd0\xce\xb5\x8eS\x89\xfd\xa9q\x88t\xe6W\x9f\xfb\x9a\xd2{\xdf^5\xa1j,6l,\x0f\x0cf\x7f0\xd4\xb2M\x9fV\xb3\x8ei\x13N\x88\x0b\xf1r\\\xdack\xdb.L\x02\x81\t\xc5\x08\xc1\xd1\xcc\x88\xb4v\x187\x03P\xad#:J\x82Pz(\x88\xb0p\xf2\x11\x16\x18O\xf5$\x9b\xdf\xdf)\xbe\xc9\x94%\xfb\x93\xe4\x1b\xe7\xc2\xc0L5>#L\x8f\x99\xd7m\xbeU\x91\xed\xabv\x7f\xe0\xb9Y\x87\x9c\xb8\xae\x8b\xa1\xdb6:\x87\xb5,C\xe1J\x03g\xdd\x97s}\xa0\xd9\xb3l\xd1\xf4\x8dn\x1f\xa8\x9b\xd6h\x8f6z#\x08\xa0\xd2\xb1j\xbd\xf7\\~\xcc\xf6o^\xbe|s\xa1\t\xf5%\x9e~LG&\x18\xf1Tv\xec\xaa\xc8\x80M\xbbDsQ]4\x92\x0f\x83Kp\x81\x10^\xda1\x07\xd4\xc7\xb3\xc9\xbd@F\x0b\t\xe6#.LN\xdbK\xc8\xc8\x8bm\xbc\xce\xb3\x8b\xbf\xc6\xe5k\x94#\x9f\xfc~)?\xed*W\xbc\xb8\xab\xdc\xf6\xc4\x8f\xcb\x94_<]c\xfc+]\xfe\xd6j\x02\xb0\xafis\x9b\x08}\x18@!\xef;\x0e\x11\x8b\xd4\xbfs\xf1X\x1c\xceO\xd8\xf1]\xec\x7fY_>D\x86\xa5\x0c\xb6\t\x88\xc6\xab\xadqS\x857\x96\xf76\x9a#\xd3\x8e\xda}\x06\x12\rh{\xf0\x93\x1f\x9f\x8a\xbc$\n%\x91\x07\x0c\xc6\x88\xa0\xb1s\xc4\xb1\xe5\xf3\xc3ML\x89\x10O\xb5\x1b\x81\x93\xf6\xe4\x0b7\x97\xd4(\x95f,\xea+*\x02}`\xe3R\xbe\xd2UN\x7f\xfa\xd8j\xae\xfe\xe8\xef\xbd\x15\xa5\xfaNz\x03Zs\xf7C@\x11b\x988a\\\xf5\x19\x92\xdc\x14\x13\x1d}so\xc7\xdc<\xb7\x9c\xdc}o\xcd!\x91\x12o\xaf\t\x0c\xc2 }\x0ei|\x8a\x8e\xe0Lf\xa4\xdf\x95Cs\xfcf\xe6\xa1g \x08\xc6\xaaQ\xe7h5~\x1c\xac\x83\x16:\xfd\xb1\xc3x\x17u\x18\xfe\xc0E\x17(\xdb\xac\xb3z9`\xf1\x85\xcaY\x04\xc0\x1e]\xe5\xfc\x7f\x9eQ\x1d\x80\x980P\x1f\xfcg\xbfs\xf0\t\x89\t\x7f\xfd\xd7\xb3\r1\x98\t\xd0\x83N82\xdf,\xe6\\\xdb>X\x1dJ\xefk\xbe\\K4y\xd3\xf5\x93\xe3kp\x8e\xb5oZ\xb7\xce\x92\xbb\xfa\xa5\xa2\x04\xaf(G=yX\xf9\xb9\xeb_\xf1\xbe\xf2\x89\xbb>Q7\x85\xcdW\x9bn\xd4Z\xef\x9e\x8a6\x12\xbb\x07\xc1\xd3:k8\xed\xb3\xfa\x1f\x99]\x1f?\x06&\x97\xf6\xac\xcb\x8cg#\x80bG\x0f\x16\xfe\xc9\xe0\xce\xfe\x1b0\xd4\x96g3\xe3H\x88\x17\xa3e\xe20FC\xe8\xc9\xd7\x07\x91\x93\x00\x94D1\xbf\xfb\x0e\xa2\x87\x0e\xd4=\xd0|\x84\x03\xf8\n}\xa1\x0f&\x18&H]\xc1\xf4\x9aUX\x1f\xf7\xe3\\\x18\xcd\xf93d\xc4\x9a\xa7\xb7\x04T%\x19\x0c\xb3/\xf7\xab\xd7\x95\xa3\xa7\xfe\xa0\x96\x9b3\xb5taB{)\xb5\x8e\xaf\x06Ra2\x08#cPZ\xdb0\x13\xa6\x9a\xf0\xf7\x19\xdd\x8f\xd5~\x14\'\xde\xd9[\xcd^\xf4\xccA\xcd\t\xae\xa8K\xae\x8bsQf\xb2Z\x97oN\xad\xa6`\xfa\':\xafD&\xa8B\xf6#\x01@hB\x1f\t\x7f\x8e\xe5p\xd1\x91\xd8\xfa\x99p4\xe8h3!\xba{\x17\xef*\xdf\xea \x00\xb9\xf5;\xae\xb0l9\x05\x03\x1e\xd0U\xeb\xf7-<\x06 \x00<\xa8\xec=\xe5\x97\xcb_\xfc\x86\xb2g\xf7A\xb5\xf9\x05fg2\x80\xbf\x1f\xb8\xe9\x03\xb50H\x16\xe1\xfb\x8f\xed\xa9\xad\xb1\xd2\xa9G\x1e\xf6\xe7;\x1a^5\xa0f\x1c\x9cek\xfcq\x8dRn\xda\xaa\xdc0OW\xf9\xf7\xca}U\x83W\x1c\xf7\xa3r\xd7"]\xe5\xf8\xa9G\x94\xd5\x7f\xb7z\x15\x16\xa6\xcc\xaa\x1ck\x9b\x00\x98?\xc9>q*\x8d\xd4\xc3\x9e\xae8q\x8cqx%D\xc4\xbe\x05\xbf2\xd0dF4<\x9dY\x87\xf5\xc3\xf8`\xbe\xb5\xcb\x04\xa4x\xd0\xbd7X\xa8\x92\xa0@\xf4\x18IAS&\rYs\x10\x9d\xd3X\xee\x86s\xa4V=\xe9\xc7\x83\xad\x19\x81\xeb\xba\xcc-&\x94\x12c\x02(9\xfc\x10\xe8;\x0e\xeb\xa9\xa1b\x11$\x9d\x98\x17;y\xb1\xf2\xdd\xbf\xf4\xd5Y\xf0\x11\x19e\xceI\xadS\x10e\x83\xd9\xf9\x06\xe2\xe3pN9\x01iu\xc6\xace\x9a-\x7f\xde\xf2\xe5\xa8\xeekk\x14J\x12\x99s\xf1\xdf\xe83\xc1w\x83^\xa1\x80%\x9a\x0b;H\xe4\xd2\xb2@\xf3\xbbz\xbe\x08C\x9a_J3t\x11\xe1\xe5Ex\x11\xa4c\x99\xb3\x9fF\x1ec\x9e\xb1x\xd6\xd3\'\xf4\xd5\xed\x9f\xfb\xc6\xbe\xd4\xda\x1b\x9a\xf2\x87\x7f_TC\x83G\\\xd7[%0I\xc8\x0e\xa3y\xc1z\xd0x\xd9s\x96-\x8b\x9c\xb6H\xd9\xe2\xfe-j\xa3H1r\xdd\x7f\xbe\xd6a\xec5\xafX\xb3\xb6\x8e~\xf3\x95\xabV\x1b^\xe2\x0c$\xc1\x86R,\xf3\xc3k\xfb*\x07\xc1\xfd7_\xfc\xe6z\xbd\xb2k\x9f\xd0)?\xe8\xbc\xce_\xa9|\xfa\xb1-Js\xc6\xd4\xb2\xd8\xe7o\xab^`\x12\x9e#\x88\xb6\tQY`\xd0\x8c\xdd\x98\xda\xf1\xa1,8\x86\xee\xaf\xc5\x9d\x0b\xf3\x13\x02$|\xbb\t\t\xa6\x8f\xe6\x1f\xe9&`\xb8\x9ay5\xce\x07O\xbf5\xa2\xb9\x13~\x03\xeb1m\xda\x83\xfb\x9f5\xe8_S\x9e\xf4i\xb4@\xd3fR\x14MK[s\xa2\xa1\x11\xcf\x8aA\x08\x08\xcf\xec\\\xfd\xab\xdeb\xd3\xfa\x1c-\xcb\xc4$D2W\x92@\x82((\x1f\xe5\xd5\xa2Dhf\xe7\xee\xaf\xd6Q\xe5\x14\xc9\':BA\xd5(\xe5 B%\xc3T\xf5\xa0\xfc\x7ft\x9a\x1e\x00\xce\xeboM`\xe6o.\xaf\xe5\xc5LU\x8c/s\xd5\xf9\xd4\xabHb\xd3\x15j\xc2\xb3\xe6\x86\x81/\xda\xbf1mE\xb1\xd2L\x86\x10\xf0\x13\xa2\x15\x15\xe3\x9b\xb0.i\xb8\xa25\x9a\xfb\x87\x04\xa0\'\xeb\xc8,\x98\xd6\x80\xd1\xe4Q\xcc\xf4\x11d\x98P\xf6\xdd\xfa\xd7\xae_\x8e\xeb\xbe\xa9\xc2)5\xfd\x1aYX(\x1b\x9d\x8e\xb5~\xd6\xba\x80)\xcf\xd4\x11\xd1\xeb\xde\xba^y\xdf\x83\x1d\xa8\xfe\xe8\xc7\xcb\xa4\xee\x1d\xea(i\x0b\xb9\xc4\xcf\x96(\xffx\xf2\x98\xf2\xd8C}\x8d>x\xf8\x0f\xf9So\xfd\x9e:\x009\xda\x04\x018\xf7\xfa\xf3__\xe7\xcd\x95;>_\xca\x1f6(\xe5\xe7\xcb\xd5\x9c\x03\xcd#8\x13A*\x84\x06\x81XT\x9a\x19\x03\xa7\x11\xa9p_$rB1\xd3cP\x1a\xab\xbf\xd7\xd5s\x92\xda\x19\x93\xd6\xdf\xf6r\xce\xd1\x8c2\x93\x06\xcc\xd15\xd2\xef\x8f\xd5\x91\x86\x9bL\x810\xa5\xf5\xf3\xec~\x87r\xe2Lk\x8f-o\x1f)p\xf19\xcc\x0e\x19bb\xb6w2\xd4\x08\x05\xcf\x9b\xea\xbe\xb6\rjm\t"\xefYw\xfb\xe8\xfb~f\xf0,AB\xe0\x0b\xf3\xe9\xf3\x07e\x8a\xb0h\xb9\xa6\xd7"\xc6_\xff\xd9\x97\x1c\x12\xd1*\rF\xde\xd6\xfc\xa0:\xec\xa0\x01\x08\x82\x8f"\xc2D\'#\x02\x05\xfdIF\xe3\xfc\xdb\xf9\xdc\xa7\xab\x19@\xeb\xcf\xb5\xed=5\x87C\xbeG__\x81\x9eJ\xaf\xe9\xdf\x984d\x13\x95\t\x05\x9f1\'\x02\x12H\xf4\xc1\xb3\xa9\xa5\x11-H8\xdckz\rG\xd2\xf2l4\xe1\xc0\x11\x1d\xa9S\xf6\x93\x16\xcd\xb4[7\x13\xe7A\x9aU\xa4\xf5\x10;;=\xe3\xa5\xe7\xda\x80\x89gN-G\xfe\xa3\xb76\xdb|\xe5\xcf_Y\xcd\x87\x8b\xffuny\xf0\x91\x83\xcaU\xcf\x9cS.z\xe6\xecr\xcaSG\xd5\x81\x1a\x0b\x9d\xb4P\xcd\xa6\x93\xb1\xb7\xc4\xa9K\xd4\xf2\xe3\xf5\xef\xd9\xb0|\xbd\xe7\xab\xf5s\x87u_Z\xed0\xdaC\xfc\xd7\xa6\xf0=\xa4\xba/\xe3\xb9\xd5\x11\x88L\xd8\x14\x84f\xf1\xc0\xc7$\x88\xc4>%\xbc\x10e\x1cY/{\xb6[\x0c\xcf>\xe2\xf0Z\xe8\xe4\x85\xaa\x1d(!j\xa1g\x1b\x97\xb6\x8b\xa0\xfa\x1f\x89-Oo}S\xcb=\xb6\xbb6\xb6G\xd2_\x87\xfa\xf9\x84\xf0@]\xe6\x82u\xc4\xec\xd6\x04\x03c\xb6\xf6\xb8o\x88!EA\x10\x9bN\xc3\x10\x83}i\x87\xec\x92SB\xdb\x82\xe9:\xff\xa8"\xd5S\x81\x86Wd\xc4\xf7\xa0\nP}\xbfN<\x1c\x91i\xb1\x86&\xed\x19E\xe0o\xda\xdf\xfdBqh\x87P\xc2\xcc\x18}\xa9g\xdb\xb6A\xad\x9c\x8c\xafzv\x1e\x04sBz9s\x80O@Y\xba\xf4sy1\x1a\xdaJDK\xeb8\x11\x14\x82A(\x90\x83\x91C\xd0\xfd\xa5\xd5Z2f\xd35jV\x18x\xfa\x7f\x8eH\xa9\xb4#"\x0c2)7\xa9\x9e\x1e\x00\xe3\x81\xc0\xa9\x81gG\xdaD\x9b\xa5\x06\xdb\x82\x88\x0e|\xe4\xf6\x8fT\xe8\xff\xd5\x9e=\xabvW\xd0\xc3)\x88\xb18s\xfa\xa6\xc2\\_3\x01\xf5\x0b\xe0\x8d\xfd\xe2E}\x05D\x90\xc3\xdb\xaf]\xabz|\rw\x00\xc95\xd5X\xe5\xd2U**Yt\x87[\xcb6\x1d\xa4\xa1\x8cT\x92\x0e\xbb\x8f\x04~e\xd3Gx\x9e\xc1\xc2gx\xa9\xec6\xb6\xdfr\x1d\xcd\xc17\xc1\xe3;\xf9gOUG\xa3a L\x18]\x8b\x9ezCW\xf9\xf0\t=\x15\xfd@1<\xca\x08\x890\x1c\xcc\xe1\'"\x82\xd0\xa67\xd7\x00\xcc\x1dKG\xd0\x8c8\x12\xfe\x1c\xea\xe7\xd3 %\xf9\xfd\xed\xe6\xa2\xcc\x80w6\xdf\xa9Z\x92\x16$\x90\xd9\xe0l|{\x02\x96c\xc2\x84\xdb\xac\r\xc1\x81Y!>\xdewv<\x01M\xb3\xe6\xe5\xbb\x04\t\xa1\x93\xf4\xe0$\x10\xb9\x07\xc2$\x85F\x99m\x91\xd6\xf6i\xe3\x05\xe5\xd8W\x0e=I[\x0boscm#\x0e\xf2S$\x92\xde\xd0#A\xa0x\x8d\xff\xcb\xf0\x184\x01-@\r\xea>D\x11\xe4!\x10P\x84\x91\xb4th\x19\x12\xa2h\x08\xfch|(\x8b\xb9\xe3\xe7P\x12\xa8\x92\xdb\x82\xa6r\x8evAR\x1a\xd4\xcc\xb0\xeaE\x1a>\xce\xaevaL\xff\xcf\xa5P!\xa3\x95HX\x9bcx\xc6:\xb7\xacW\xb6|\xecsu1\xa7\xa5%iq!\x1aU\x80\xfb]\xd2\x17\xce\xd1\x16,\xed\x8fl\xb4\xa6\x1a\xb7\xf4\x1cYv=\xef\xe9\xaa\xa5\xcb{\xbbj!\x90\x88\x80j2Q\x05\x9b\x89\xc82\xd1\x95\x10\x83\x00\xfa\xb2\x0eo\xaa\xa8Cy\xb1\xf0\xa2A gvu\x95;\x16\xea\xaa%\xc4\xb7^tv\xf9\xd3z\xeb\xd5\x8e\xb2\xcc\x92\xad\xce\x9cZ\xfb\xfde3\x06Kp\x01\xffT\x9c\xd1X\x18`\xecv`\xf69R-\x87\x060"3\x8d\x03N\xc3O\x8d9iC\x9a\x1f,\x97b\xce\xc9\xc7[\x9eRr\xc2\x19\xb1\xb3\x9f\xa10L\xff\xb2mn\xad\x89>A\x0fi\x996\x9a\xaa<\xcc\x94y\x02h\x0bmHU\xd7\xe6\x8e\xe2\xe2_RK"\xfd\x9c\x7fK{<\x0c-\xa3\x94\xaf\x00R\xd0\xdb\x90 \xa0\xb4\x84\xa1_\xbe\xf5m\xd5T5\x06\x0e\x8d-\xb9\xeb}\xf5yF\xbb\xa6\t\xc9\xa2\xa9\xf8b\x08I\xfc\x16\xde\xec_!8\xe6G<\x8e^\xedn2m\xa9\x93\xca\xb9\x08\x87\xc0,\x92\x94\xc3O\x9b.\x040XqH\x1a\x91\xd8\x10\x90\xd1F@\x07lB\t\x1a\xc5p\x99\x14\xed3\x84\x1c\xb4\x83\t\xe3\x9c\x1e\xa9m\xee{\xf8\xc2\xf9\\/\x93\x990;G\xbb\xa8D\xdf\\\xc5\x87k\x83\x13S\xa4\xddO\xc6\xa5\xe1\xb1\x81\xaeK\tg\xb2\xd5@\xad\xee\xff\xd7\x11\xe7\x1eXN\xf3&\x93j\xb0PP\xff#\x19G\t\xa3y\xa8\x84\x9b\xbc\x12\xa7O\xbc\x14\x93\xc9\xb4\x93l\xc1\x06\xe3\xe9\xe7\xe8\xab\rB\xb6\xbe\xb125a\x00\xcas\x18\xaa\xae\x03\xb9D\x03\xd8\xeb4\xbe\xef\xbb\x1e\x14\xa0K\xac\xccB\xe3\xbd\x845%\x1b\x95\x8b\xdfQ{\x10\xd4\x9e\x81\xe7\xafT\xb6\xe9\xfe|\xd5L\xcb\xfeb\xd9\xb2\xc2\xefW.\x9fyl\x8b\xea\xa4$\xd1\xdd\x1f\xbfC\xdaN\r\xab\xaf\xfalr\x8c\x95\x00\x88c\x13\x93\xb4\x99\x1f\xaaK\x06 \xc1\x1e\xc72\x02MOE?g\x07\xe1\x19\x94\xc3\x01\x8ey(\xa9\xa4P\x87\xc63]\xb8\x1d\xdf\x1fN\xean\xe6\x04\xa4]\x9b\xb5B\x83\xd2\xace,\xf6\x15\xe0\xdd\\}\x15L\x15\xdd\x94\xdf\xda\xf4\r\xb0!\x840?\x81\xe9\xde\x98Z\xcc\x82D\xeb0\xbf\xbd!\xc4\x98\x12i\'\x9e\xb5\xff?\x08\x16\xc3\n\x0791\xc9,\xa6\xca\x9b;\xd4\x87\t\xfc\xcbD\x9d\xc4\xea3t\xc1\xa2\xb53\xa3h\x03\x8c\xcb\xbb\xaa{\x8f>\x00|\x07*\xae\x94yjQ\xbe\xe4iKV/-\xa4\x00"\xa9Nc\x0e\xf0\xfc\x13\x0c\x12;<0\'\xa2P\xa4L1\xfd\xe2W\xbbl\xb5\xb2\xc0\t\x0b\xd4D\x11\x9eZ\x0e\x1e3\x0b\xf8\x07\x96m\xce\xac\x1d\x88\xe5=\xec\xdd\xb3{\xdf\x18\xf0\xae\xff]\x92\x99\x8a\xc0\xe9J\xcd\xd9\xf8\xe8\xcf\x84\xb3\x03S\x8e\xd7\x81\xa1\xd0-\xe5\x95\xd7X\x84v3\xd9\xd7\x8b\xe9L\xe1d\x06 \xa4\xc1\xd7`\xcc\x99\xc8\n\x06f\x9a\x9aJ\xc5\x81JP\x10B\xde\xc7\xf8>\x07\x15\xb4#\x07\x90\xcd\xa7\x9b\xad+\xea\xc2{ib\xeb\xda\xff\xc7\x91\xf8\xbc\x00x^\x00\x8c\xd7\xbd\xcc\xea\xc7\x7f\xbc\x00\x00[Rr8\xdc\x87Hz(On_\xad\xc0e5$\x07\xee\'\x01\xc4\x03y\xb06 90 %
+ results = instance.search_satellite_imagery('test_pol', 1480699083, 1480782083, ImageTypeEnum.PNG,
+ PresetEnum.EVI, 10, 20, SatelliteEnum.SENTINEL_2.symbol, 0,
+ 10, 90, 100)
+ self.assertEqual(2, len(results))
+ self.assertTrue(all([i.image_type == ImageTypeEnum.PNG and i.preset == PresetEnum.EVI for i in results]))
+
+ # all Sentinel2 EVI images available for the polygon, 10 < px/m < 20, cloud coverage < 10%, data doverage > 90 %
+ results = instance.search_satellite_imagery('test_pol', 1480699083, 1480782083, None,
+ PresetEnum.EVI, 10, 20, SatelliteEnum.SENTINEL_2.symbol, 0,
+ 10, 90, 100)
+ self.assertEqual(3, len(results))
+ self.assertTrue(all([i.preset == PresetEnum.EVI for i in results]))
+
+ def test_repr(self):
+ instance = AgroManager('APIKey')
+ repr(instance)
diff --git a/tests/unit/agroapi10/test_imagery.py b/tests/unit/agroapi10/test_imagery.py
new file mode 100644
index 00000000..d0438a73
--- /dev/null
+++ b/tests/unit/agroapi10/test_imagery.py
@@ -0,0 +1,95 @@
+import unittest
+from datetime import datetime
+from pyowm.commons.image import Image, ImageTypeEnum
+from pyowm.agroapi10.enums import PresetEnum, SatelliteEnum
+from pyowm.agroapi10.imagery import MetaImage, SatelliteImage
+from pyowm.utils.timeformatutils import UTC
+
+
+class TestMetaImage(unittest.TestCase):
+
+ test_acquisition_time = 1378459200
+ test_iso_acquisition_time = "2013-09-06 09:20:00+00"
+ test_date_acquisition_time = datetime.strptime(test_iso_acquisition_time, '%Y-%m-%d %H:%M:%S+00').replace(
+ tzinfo=UTC())
+
+ test_instance = MetaImage('http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, test_acquisition_time, 98.2, 0.3, 11.7, 7.89,
+ polygon_id='my_polygon', stats_url='http://stats.com')
+
+ def test_init_fails_with_wrong_parameters(self):
+ self.assertRaises(AssertionError, MetaImage, None, PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, self.test_acquisition_time, 98.2, 0.3, 11.7, 7.89)
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, 'a_string', 98.2, 0.3, 11.7, 7.89)
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, -567, 98.2, 0.3, 11.7, 7.89)
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, self.test_acquisition_time, 'a_string', 0.3, 11.7, 7.89)
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, self.test_acquisition_time, -32.1, 0.3, 11.7, 7.89)
+
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, self.test_acquisition_time, 98.2, 'a_string', 11.7, 7.89)
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, self.test_acquisition_time, 98.2, -21.1, 11.7, 7.89)
+ # sun azimuth
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, self.test_acquisition_time, 98.2, 21.1, 'a_string', 7.89)
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2, self.test_acquisition_time, 98.2, 21.1, -54.4, 7.89)
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, self.test_acquisition_time, 98.2, 21.1, 368.4, 7.89)
+ # sun elevation
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, self.test_acquisition_time, 98.2, 21.1, 54.4, 'a_string')
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, self.test_acquisition_time, 98.2, 21.1, 54.4, -32.2)
+ self.assertRaises(AssertionError, MetaImage, 'http://a.com', PresetEnum.FALSE_COLOR,
+ SatelliteEnum.SENTINEL_2.name, self.test_acquisition_time, 98.2, 21.1, 54.4, 100.3)
+
+ def test_acquisition_time_returning_different_formats(self):
+
+ self.assertEqual(self.test_instance.acquisition_time(timeformat='unix'),
+ self.test_acquisition_time)
+ self.assertEqual(self.test_instance.acquisition_time(timeformat='iso'),
+ self.test_iso_acquisition_time)
+ self.assertEqual(self.test_instance.acquisition_time(timeformat='date'),
+ self.test_date_acquisition_time)
+
+ def test_acquisition_time_fails_with_unknown_timeformat(self):
+ self.assertRaises(ValueError, MetaImage.acquisition_time, self.test_instance, 'xyz')
+
+ def test_repr(self):
+ repr(self.test_instance)
+
+
+class TestSatelliteImage(unittest.TestCase):
+
+ test_image = Image(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01\x00\x00\x00\x01', ImageTypeEnum.PNG)
+ test_instance = SatelliteImage(TestMetaImage.test_instance, test_image,
+ downloaded_on=TestMetaImage.test_acquisition_time)
+
+ def test_init_fails_with_wrong_parameters(self):
+ with self.assertRaises(AssertionError):
+ SatelliteImage(None, self.test_image)
+ with self.assertRaises(AssertionError):
+ SatelliteImage(TestMetaImage.test_instance, None)
+ with self.assertRaises(AssertionError):
+ SatelliteImage(TestMetaImage.test_instance, self.test_image, downloaded_on='not_an_int')
+ with self.assertRaises(AssertionError):
+ SatelliteImage(TestMetaImage.test_instance, self.test_image, downloaded_on=1234567, palette=888)
+
+ def test_downloaded_on_returning_different_formats(self):
+ self.assertEqual(self.test_instance.downloaded_on(timeformat='unix'),
+ TestMetaImage.test_acquisition_time)
+ self.assertEqual(self.test_instance.downloaded_on(timeformat='iso'),
+ TestMetaImage.test_iso_acquisition_time)
+ self.assertEqual(self.test_instance.downloaded_on(timeformat='date'),
+ TestMetaImage.test_date_acquisition_time)
+
+ def test_downloaded_on_fails_with_unknown_timeformat(self):
+ self.assertRaises(ValueError, SatelliteImage.downloaded_on, self.test_instance, 'xyz')
+
+ def test_repr(self):
+ repr(self.test_instance)
diff --git a/tests/unit/agroapi10/test_polygon.py b/tests/unit/agroapi10/test_polygon.py
new file mode 100644
index 00000000..f7039b99
--- /dev/null
+++ b/tests/unit/agroapi10/test_polygon.py
@@ -0,0 +1,91 @@
+import unittest
+from pyowm.agroapi10.polygon import Polygon, GeoPoint, GeoPolygon
+
+
+class TestPolygon(unittest.TestCase):
+
+ geopoint= GeoPoint(34, -56.3)
+ geopolygon = GeoPolygon([
+ [[2.3, 57.32], [23.19, -20.2], [-120.4, 19.15], [2.3, 57.32]]
+ ])
+
+ def test_polygon_fails_with_wrong_parameters(self):
+
+ self.assertRaises(AssertionError, Polygon, None, 'polygon', self.geopolygon, self.geopoint, 123.4, 'user')
+ self.assertRaises(AssertionError, Polygon, 'id', 'polygon', 'wrong', self.geopoint, 123.4, 'user')
+ self.assertRaises(AssertionError, Polygon, None, 'polygon', self.geopolygon, 'wrong', 123.4, 'user')
+ self.assertRaises(AssertionError, Polygon, None, 'polygon', self.geopolygon, self.geopoint, None, 'user')
+ self.assertRaises(AssertionError, Polygon, None, 'polygon', self.geopolygon, self.geopoint, -77, 'user')
+
+ def test_area_kilometers_property(self):
+ area_hs = 456.78
+ expected = area_hs * 0.01
+ instance = Polygon('id', 'polygon', self.geopolygon, self.geopoint, area_hs, 'user')
+ self.assertEqual(expected, instance.area_km)
+ instance = Polygon('id', 'polygon', self.geopolygon, self.geopoint, None, 'user')
+ self.assertIsNone(instance.area_km)
+
+ def test_from_dict(self):
+ _id = "5abb9fb82c8897000bde3e87"
+ name = "Polygon Sample"
+ coords = [121.1867, 37.6739]
+ geopolygon = GeoPolygon([[
+ [-121.1958, 37.6683],
+ [-121.1779, 37.6687],
+ [-121.1773, 37.6792],
+ [-121.1958, 37.6792],
+ [-121.1958, 37.6683]]])
+ center = GeoPoint(coords[0], coords[1])
+ area = 190.6343
+ user_id = "557066d0ff7a7e3897531d94"
+ the_dict = {
+ "id": _id,
+ "geo_json": {
+ "type": "Feature",
+ "properties": {
+
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [-121.1958, 37.6683],
+ [-121.1779, 37.6687],
+ [-121.1773, 37.6792],
+ [-121.1958, 37.6792],
+ [-121.1958, 37.6683]
+ ]
+ ]
+ }
+ },
+ "name": name,
+ "center": coords,
+ "area": area,
+ "user_id": user_id
+ }
+ expected = Polygon(_id, name, geopolygon, center, area, user_id)
+ result = Polygon.from_dict(the_dict)
+ self.assertEqual(expected.id, result.id)
+ self.assertEqual(expected.name, result.name)
+ self.assertEqual(expected.area, result.area)
+ self.assertEqual(expected.user_id, result.user_id)
+ self.assertEqual(expected.center.lat, result.center.lat)
+ self.assertEqual(expected.center.lon, result.center.lon)
+ self.assertEqual(expected.geopolygon.geojson(), result.geopolygon.geojson())
+
+ # now testing with dirty data
+ self.assertRaises(AssertionError, Polygon.from_dict, None)
+
+ the_dict['center'] = ['no_lon', 'no_lat']
+ self.assertRaises(ValueError, Polygon.from_dict, the_dict)
+ the_dict['center'] = coords
+
+ del the_dict['id']
+ self.assertRaises(AssertionError, Polygon.from_dict, the_dict)
+
+ def test_repr(self):
+ instance = Polygon('id', 'polygon', self.geopolygon, self.geopoint, 1.2, 'user')
+ repr(instance)
+ instance = Polygon('id')
+ repr(instance)
+
diff --git a/tests/unit/agroapi10/test_search.py b/tests/unit/agroapi10/test_search.py
new file mode 100644
index 00000000..3cbc5482
--- /dev/null
+++ b/tests/unit/agroapi10/test_search.py
@@ -0,0 +1,110 @@
+import unittest
+import json
+from datetime import datetime
+from pyowm.commons.enums import ImageTypeEnum
+from pyowm.agroapi10.search import SatelliteImagerySearchResultSet
+from pyowm.agroapi10.enums import PresetEnum
+from pyowm.utils.timeformatutils import UTC
+
+
+class TestSatelliteImagerySearchResultSet(unittest.TestCase):
+
+ test_data = json.loads('''[{
+ "dt":1500940800,
+ "type":"Landsat 8",
+ "dc":100,
+ "cl":1.56,
+ "sun":{
+ "azimuth":126.742,
+ "elevation":63.572},
+ "image":{
+ "truecolor":"http://api.agromonitoring.com/image/1.0/00059768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7",
+ "falsecolor":"http://api.agromonitoring.com/image/1.0/01059768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7",
+ "ndvi":"http://api.agromonitoring.com/image/1.0/02059768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7",
+ "evi":"http://api.agromonitoring.com/image/1.0/03059768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7"},
+ "tile":{
+ "truecolor":"http://api.agromonitoring.com/tile/1.0/{z}/{x}/{y}/00059768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7",
+ "falsecolor":"http://api.agromonitoring.com/tile/1.0/{z}/{x}/{y}/01059768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7",
+ "ndvi":"http://api.agromonitoring.com/tile/1.0/{z}/{x}/{y}/02059768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7",
+ "evi":"http://api.agromonitoring.com/tile/1.0/{z}/{x}/{y}/03059768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7"},
+ "stats":{
+ "ndvi":"http://api.agromonitoring.com/stats/1.0/02359768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7",
+ "evi":"http://api.agromonitoring.com/stats/1.0/03359768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7"},
+ "data":{
+ "truecolor":"http://api.agromonitoring.com/data/1.0/00159768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7",
+ "falsecolor":"http://api.agromonitoring.com/data/1.0/01159768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7",
+ "ndvi":"http://api.agromonitoring.com/data/1.0/02259768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7",
+ "evi":"http://api.agromonitoring.com/data/1.0/03259768a00/5ac22f004b1ae4000b5b97cf?appid=bb0664ed43c153aa072c760594d775a7"}}]''')
+ test_issuing_time = 1378459200
+ test_iso_issuing_time = "2013-09-06 09:20:00+00"
+ test_date_issuing_time = datetime.strptime(test_iso_issuing_time, '%Y-%m-%d %H:%M:%S+00').replace(tzinfo=UTC())
+
+ test_instance = SatelliteImagerySearchResultSet('my_polygon', test_data, test_issuing_time)
+
+ def test_instantiation_fails_with_wrong_arguments(self):
+ self.assertRaises(AssertionError, SatelliteImagerySearchResultSet, None, [], 1234567)
+ self.assertRaises(AssertionError, SatelliteImagerySearchResultSet, 'my_polygon', None, 1234567)
+ self.assertRaises(AssertionError, SatelliteImagerySearchResultSet, 'my_polygon', [], None)
+
+ def test_instantiation(self):
+ self.assertEqual(12, len(self.test_instance.metaimages))
+ self.assertTrue(all([mi.stats_url is not None for mi in self.test_instance.metaimages if mi.preset in
+ [PresetEnum.EVI, PresetEnum.NDVI]]))
+
+ def test_issued_on_returning_different_formats(self):
+ self.assertEqual(self.test_instance.issued_on(timeformat='unix'),
+ self.test_issuing_time)
+ self.assertEqual(self.test_instance.issued_on(timeformat='iso'),
+ self.test_iso_issuing_time)
+ self.assertEqual(self.test_instance.issued_on(timeformat='date'),
+ self.test_date_issuing_time)
+
+ def test_issued_on_fails_with_unknown_timeformat(self):
+ self.assertRaises(ValueError, SatelliteImagerySearchResultSet.issued_on, self.test_instance, 'xyz')
+
+ def test_all(self):
+ result = self.test_instance.all()
+ self.assertEqual(result, self.test_instance.metaimages)
+
+ def test_with_img_type(self):
+ # failure
+ with self.assertRaises(AssertionError):
+ self.test_instance.with_img_type(1234)
+
+ # success
+ result = self.test_instance.with_img_type(ImageTypeEnum.PNG)
+ self.assertEqual(8, len(result))
+ result = self.test_instance.with_img_type(ImageTypeEnum.GEOTIFF)
+ self.assertEqual(4, len(result))
+
+ def test_with_preset(self):
+ # failure
+ with self.assertRaises(AssertionError):
+ self.test_instance.with_preset(1234)
+
+ # success
+ result = self.test_instance.with_preset(PresetEnum.TRUE_COLOR)
+ self.assertEqual(3, len(result))
+ result = self.test_instance.with_preset(PresetEnum.FALSE_COLOR)
+ self.assertEqual(3, len(result))
+ result = self.test_instance.with_preset(PresetEnum.NDVI)
+ self.assertEqual(3, len(result))
+ result = self.test_instance.with_preset(PresetEnum.EVI)
+ self.assertEqual(3, len(result))
+
+ def test_with_img_type_and_preset(self):
+ # failure
+ with self.assertRaises(AssertionError):
+ self.test_instance.with_img_type_and_preset(1234, 1234)
+ with self.assertRaises(AssertionError):
+ self.test_instance.with_img_type_and_preset(1234, PresetEnum.TRUE_COLOR)
+ with self.assertRaises(AssertionError):
+ self.test_instance.with_img_type_and_preset(ImageTypeEnum.PNG, 1234)
+
+ # success
+ result = self.test_instance.with_img_type_and_preset(ImageTypeEnum.PNG, PresetEnum.TRUE_COLOR)
+ self.assertEqual(2, len(result))
+ result = self.test_instance.with_img_type_and_preset(ImageTypeEnum.GEOTIFF, PresetEnum.EVI)
+ self.assertEqual(1, len(result))
+ result = self.test_instance.with_img_type_and_preset(ImageTypeEnum.GEOTIFF, PresetEnum.FALSE_COLOR)
+ self.assertEqual(1, len(result))
diff --git a/tests/unit/agroapi10/test_soil.py b/tests/unit/agroapi10/test_soil.py
new file mode 100644
index 00000000..8418549b
--- /dev/null
+++ b/tests/unit/agroapi10/test_soil.py
@@ -0,0 +1,88 @@
+import unittest
+from datetime import datetime
+from pyowm.agroapi10.soil import Soil
+from pyowm.utils.timeformatutils import UTC
+
+
+class TestSoil(unittest.TestCase):
+
+ test_reference_time = 1378459200
+ test_iso_reference_time = "2013-09-06 09:20:00+00"
+ test_date_reference_time = datetime.strptime(test_iso_reference_time, '%Y-%m-%d %H:%M:%S+00').replace(
+ tzinfo=UTC())
+ test_temperature = 294.199
+ test_celsius_temperature = 21.049
+ test_fahrenheit_temperature = 69.888
+ test_instance = Soil(test_reference_time, test_temperature, test_temperature, 45.6, 'my-polygon')
+
+ def test_soil_fails_with_wrong_parameters(self):
+ self.assertRaises(AssertionError, Soil, None, 12.4, 11.8, 80.2, 'my-polygon')
+ self.assertRaises(AssertionError, Soil, 'wrong', 12.4, 11.8, 80.2, 'my-polygon')
+ self.assertRaises(ValueError, Soil, -345, 12.4, 11.8, 80.2, 'my-polygon')
+ self.assertRaises(AssertionError, Soil, 1234567, None, 11.8, 80.2, 'my-polygon')
+ self.assertRaises(AssertionError, Soil, 1234567, 'wrong', 11.8, 80.2, 'my-polygon')
+ self.assertRaises(AssertionError, Soil, 1234567, 12.4, None, 80.2, 'my-polygon')
+ self.assertRaises(AssertionError, Soil, 1234567, 12.4, 'wrong', 80.2, 'my-polygon')
+ self.assertRaises(AssertionError, Soil, 1234567, 12.4, 11.8, None, 'my-polygon')
+ self.assertRaises(AssertionError, Soil, 1234567, 12.4, 11.8, "'wrong", 'my-polygon')
+ self.assertRaises(ValueError, Soil, 1234567, 12.4, 11.8, -45.6, 'my-polygon')
+
+ def test_reference_time_returning_different_formats(self):
+
+ self.assertEqual(self.test_instance.reference_time(timeformat='unix'),
+ self.test_reference_time)
+ self.assertEqual(self.test_instance.reference_time(timeformat='iso'),
+ self.test_iso_reference_time)
+ self.assertEqual(self.test_instance.reference_time(timeformat='date'),
+ self.test_date_reference_time)
+
+ def test_reference_time_fails_with_unknown_timeformat(self):
+ self.assertRaises(ValueError, Soil.reference_time, self.test_instance, 'xyz')
+
+ def test_from_dict(self):
+ ref_time = 12345567
+ surf_temp = 11.2
+ ten_cm_temp = 9.5
+ moisture = 8.2
+ pol_id = "5abb9fb82c8897000bde3e87"
+ the_dict = {
+ "reference_time": ref_time,
+ "surface_temp": surf_temp,
+ "ten_cm_temp": ten_cm_temp,
+ "moisture": moisture,
+ "polygon_id": pol_id
+ }
+ expected = Soil(ref_time, surf_temp, ten_cm_temp, moisture, pol_id)
+ result = Soil.from_dict(the_dict)
+ self.assertEqual(expected.reference_time(), result.reference_time())
+ self.assertEqual(expected.surface_temp(), result.surface_temp())
+ self.assertEqual(expected.ten_cm_temp(), result.ten_cm_temp())
+ self.assertEqual(expected.moisture, result.moisture)
+ self.assertEqual(expected.polygon_id, result.polygon_id)
+
+ def test_returning_different_units_for_temperatures(self):
+ # Surface temp
+ result_kelvin = self.test_instance.surface_temp(unit='kelvin')
+ result_celsius = self.test_instance.surface_temp(unit='celsius')
+ result_fahrenheit = self.test_instance.surface_temp(unit='fahrenheit')
+ self.assertAlmostEqual(result_kelvin, self.test_temperature, delta=0.1)
+ self.assertAlmostEqual(result_celsius, self.test_celsius_temperature, delta=0.1)
+ self.assertAlmostEqual(result_fahrenheit, self.test_fahrenheit_temperature, delta=0.1)
+
+ # 10 cm temp
+ result_kelvin = self.test_instance.ten_cm_temp(unit='kelvin')
+ result_celsius = self.test_instance.ten_cm_temp(unit='celsius')
+ result_fahrenheit = self.test_instance.ten_cm_temp(unit='fahrenheit')
+ self.assertAlmostEqual(result_kelvin, self.test_temperature, delta=0.1)
+ self.assertAlmostEqual(result_celsius, self.test_celsius_temperature, delta=0.1)
+ self.assertAlmostEqual(result_fahrenheit, self.test_fahrenheit_temperature, delta=0.1)
+
+ def test_trying_unknown_units_for_temperatures(self):
+ self.assertRaises(ValueError, Soil.surface_temp, self.test_instance, 'xyz')
+ self.assertRaises(ValueError, Soil.ten_cm_temp, self.test_instance, 'xyz')
+
+ def test_repr(self):
+ instance = Soil(1234567, 12.4, 11.8, 80.2, 'my-polygon')
+ repr(instance)
+ instance = Soil(1234567, 12.4, 11.8, 80.2, None)
+ repr(instance)
\ No newline at end of file
diff --git a/tests/unit/commons/test_databoxes.py b/tests/unit/commons/test_databoxes.py
new file mode 100644
index 00000000..d0d140e0
--- /dev/null
+++ b/tests/unit/commons/test_databoxes.py
@@ -0,0 +1,17 @@
+import unittest
+from pyowm.commons.databoxes import ImageType, Satellite
+
+
+class TestImageType(unittest.TestCase):
+
+ def test_repr(self):
+ instance = ImageType('PDF', 'application/pdf')
+ repr(instance)
+
+
+class TestSatellite(unittest.TestCase):
+
+ def test_repr(self):
+ instance = Satellite('Terrasat', 'tst')
+ repr(instance)
+
diff --git a/tests/unit/commons/test_enums.py b/tests/unit/commons/test_enums.py
new file mode 100644
index 00000000..c226bc05
--- /dev/null
+++ b/tests/unit/commons/test_enums.py
@@ -0,0 +1,23 @@
+import unittest
+from pyowm.commons.enums import ImageTypeEnum
+from pyowm.commons.databoxes import ImageType
+
+
+class TestImageTypeEnum(unittest.TestCase):
+
+ def test_lookup_by_mime_type(self):
+ mime_not_found = 'unexistent/xyz'
+ mime_found = 'image/png'
+ result = ImageTypeEnum.lookup_by_mime_type(mime_found)
+ self.assertTrue(isinstance(result, ImageType))
+ result = ImageTypeEnum.lookup_by_mime_type(mime_not_found)
+ self.assertIsNone(result)
+
+ def test_lookup_by_name(self):
+ name_not_found = 'ZOOMOOO'
+ name_found = 'GEOTIFF'
+ result = ImageTypeEnum.lookup_by_name(name_found)
+ self.assertTrue(isinstance(result, ImageType))
+ result = ImageTypeEnum.lookup_by_name(name_not_found)
+ self.assertIsNone(result)
+
diff --git a/tests/unit/commons/test_http_client.py b/tests/unit/commons/test_http_client.py
index 28704a89..278d17c3 100644
--- a/tests/unit/commons/test_http_client.py
+++ b/tests/unit/commons/test_http_client.py
@@ -11,6 +11,7 @@ class MockResponse:
def __init__(self, status, payload):
self.status_code = status
self.text = payload
+ self.content = payload
def json(self):
return json.loads(self.text)
@@ -233,3 +234,28 @@ def monkey_patched_get_timeouting(uri, params=None, headers=None,
except api_call_error.APICallTimeoutError:
requests.get = self.requests_original_get
+ def test_get_png(self):
+ expected_data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x01\x03\x00\x00\x00%\xdbV\xca\x00\x00\x00\x03PLTE\x00p\xff\xa5G\xab\xa1\x00\x00\x00\x01tRNS\xcc\xd24V\xfd\x00\x00\x00\nIDATx\x9ccb\x00\x00\x00\x06\x00\x0367|\xa8\x00\x00\x00\x00IEND\xaeB`\x82'
+
+ def monkey_patched_get(uri, stream=True, params=None, headers=None, timeout=None,
+ verify=False):
+ return MockResponse(200, expected_data)
+
+ requests.get = monkey_patched_get
+ status, data = HttpClient().get_png('http://anyurl.com')
+ self.assertIsInstance(data, bytes)
+ self.assertEqual(expected_data, data)
+ requests.get = self.requests_original_get
+
+ def test_get_geotiff(self):
+ expected_data = b'II*\x00\x08\x00\x04\x00k{\x84s\x84\x84\x8c\x84\x84\x84k\x84k\x84\x84k{s\x9c\x94k\x84'
+
+ def monkey_patched_get(uri, stream=True, params=None, headers=None, timeout=None,
+ verify=False):
+ return MockResponse(200, expected_data)
+
+ requests.get = monkey_patched_get
+ status, data = HttpClient().get_geotiff('http://anyurl.com')
+ self.assertIsInstance(data, bytes)
+ self.assertEqual(expected_data, data)
+ requests.get = self.requests_original_get
diff --git a/tests/unit/commons/test_tile.py b/tests/unit/commons/test_tile.py
new file mode 100644
index 00000000..4f98b8fa
--- /dev/null
+++ b/tests/unit/commons/test_tile.py
@@ -0,0 +1,20 @@
+import unittest
+from pyowm.commons.tile import Tile
+from pyowm.utils.geo import Polygon
+from pyowm.commons.image import Image
+
+
+class TestTile(unittest.TestCase):
+
+ def test_instantiation_fails_with_wrong_arguments(self):
+ i = Image(b'x/1')
+ self.assertRaises(AssertionError, Tile, -1, 2, 3, 'layer', i)
+ self.assertRaises(AssertionError, Tile, 1, -2, 3, 'layer', i)
+ self.assertRaises(AssertionError, Tile, 1, 2, -3, 'layer', i)
+ self.assertRaises(AssertionError, Tile, 1, 2, 3, 'layer', 'not-an-image')
+
+ def test_bounding_box(self):
+ instance = Tile(0, 0, 18, 'temperature', Image(b'x/1'))
+ result = instance.bounding_polygon()
+ self.assertIsInstance(result, Polygon)
+ print(result.geojson())
\ No newline at end of file
diff --git a/tests/unit/pollutionapi30/test_coindex.py b/tests/unit/pollutionapi30/test_coindex.py
index 5277a4e9..b3c37ca4 100644
--- a/tests/unit/pollutionapi30/test_coindex.py
+++ b/tests/unit/pollutionapi30/test_coindex.py
@@ -1,6 +1,6 @@
import unittest
from datetime import datetime
-from pyowm.webapi25.location import Location
+from pyowm.weatherapi25.location import Location
from pyowm.pollutionapi30.coindex import COIndex
from pyowm.utils.timeformatutils import UTC, _datetime_to_UNIXtime
from tests.unit.pollutionapi30.json_test_dumps import COINDEX_JSON_DUMP
diff --git a/tests/unit/pollutionapi30/test_no2index.py b/tests/unit/pollutionapi30/test_no2index.py
index 6cd2ba88..f5c29d00 100644
--- a/tests/unit/pollutionapi30/test_no2index.py
+++ b/tests/unit/pollutionapi30/test_no2index.py
@@ -1,6 +1,6 @@
import unittest
from datetime import datetime
-from pyowm.webapi25.location import Location
+from pyowm.weatherapi25.location import Location
from pyowm.pollutionapi30.no2index import NO2Index
from pyowm.utils.timeformatutils import UTC, _datetime_to_UNIXtime
from tests.unit.pollutionapi30.json_test_dumps import NO2INDEX_JSON_DUMP
diff --git a/tests/unit/pollutionapi30/test_ozone.py b/tests/unit/pollutionapi30/test_ozone.py
index 44aa26e4..085357ee 100644
--- a/tests/unit/pollutionapi30/test_ozone.py
+++ b/tests/unit/pollutionapi30/test_ozone.py
@@ -1,6 +1,6 @@
import unittest
from datetime import datetime
-from pyowm.webapi25.location import Location
+from pyowm.weatherapi25.location import Location
from pyowm.pollutionapi30.ozone import Ozone
from pyowm.utils.timeformatutils import UTC, _datetime_to_UNIXtime
from tests.unit.pollutionapi30.json_test_dumps import OZONE_JSON_DUMP
diff --git a/tests/unit/pollutionapi30/test_so2index.py b/tests/unit/pollutionapi30/test_so2index.py
index 561a1f42..47cf92f4 100644
--- a/tests/unit/pollutionapi30/test_so2index.py
+++ b/tests/unit/pollutionapi30/test_so2index.py
@@ -1,6 +1,6 @@
import unittest
from datetime import datetime
-from pyowm.webapi25.location import Location
+from pyowm.weatherapi25.location import Location
from pyowm.pollutionapi30.so2index import SO2Index
from pyowm.utils.timeformatutils import UTC, _datetime_to_UNIXtime
from tests.unit.pollutionapi30.json_test_dumps import SO2INDEX_JSON_DUMP
diff --git a/tests/unit/stationsapi30/test_stations_manager.py b/tests/unit/stationsapi30/test_stations_manager.py
index 86cf5544..61c06ed6 100644
--- a/tests/unit/stationsapi30/test_stations_manager.py
+++ b/tests/unit/stationsapi30/test_stations_manager.py
@@ -268,3 +268,39 @@ def test_send_buffer_failing(self):
with self.assertRaises(AssertionError):
instance.send_buffer(None)
+
+ def test__structure_dict(self):
+ temp = dict(min=0, max=100)
+ msmt = Measurement('test_station', 1378459200,
+ temperature=temp, wind_speed=2.1, wind_gust=67,
+ humidex=77, weather_other=dict(key='val'))
+ expected = {
+ 'station_id': 'test_station',
+ 'dt': 1378459200,
+ 'temperature': temp,
+ 'wind_speed': 2.1,
+ 'wind_gust': 67,
+ 'humidex': 77,
+ 'weather': [
+ {
+ 'other': {
+ 'key': 'val'
+ }
+ }
+ ]
+ }
+ instance = StationsManager('API-Key')
+ result = instance._structure_dict(msmt)
+ self.assertEqual(expected['station_id'], result['station_id'])
+ self.assertEqual(expected['dt'], result['dt'])
+ self.assertEqual(expected['wind_speed'], result['wind_speed'])
+ self.assertEqual(expected['wind_gust'], result['wind_gust'])
+ self.assertEqual(expected['humidex'], result['humidex'])
+ self.assertEqual(expected['temperature'], result['temperature'])
+ for item in result['weather']:
+ content = item.get('other')
+ if content:
+ self.assertEqual(expected['weather'][0]['other'], content)
+ return
+ self.fail()
+
diff --git a/pyowm/webapi25/parsers/so2indexparser.py b/tests/unit/tiles/__init__.py
similarity index 100%
rename from pyowm/webapi25/parsers/so2indexparser.py
rename to tests/unit/tiles/__init__.py
diff --git a/tests/unit/tiles/test_tile_manager.py b/tests/unit/tiles/test_tile_manager.py
new file mode 100644
index 00000000..21af118d
--- /dev/null
+++ b/tests/unit/tiles/test_tile_manager.py
@@ -0,0 +1,29 @@
+import unittest
+from pyowm.commons.http_client import HttpClient
+from pyowm.tiles.tile_manager import TileManager
+from pyowm.commons.tile import Tile
+from pyowm.tiles.enums import MapLayerEnum
+
+
+class MockHttpClientReturningTile(HttpClient):
+
+ d = b'1234567890'
+
+ def get_png(self, uri, params=None, headers=None):
+ return 200, self.d
+
+
+class TestTileManager(unittest.TestCase):
+
+ def test_instantiation_fails_with_wrong_arguments(self):
+ self.assertRaises(AssertionError, TileManager, None, MapLayerEnum.PRESSURE)
+ self.assertRaises(AssertionError, TileManager, 'apikey', None)
+ self.assertRaises(AssertionError, TileManager, 'apikey', 1234)
+
+ def test_get_tile(self):
+ mocked = MockHttpClientReturningTile()
+ instance = TileManager('Api_key', 'a_layer')
+ instance.http_client = mocked
+ result = instance.get_tile(1, 2, 3)
+ self.assertIsInstance(result, Tile)
+ self.assertEqual(mocked.d, result.image.data)
diff --git a/tests/unit/utils/test_geo.py b/tests/unit/utils/test_geo.py
index 7b592fb1..8ba01ecb 100644
--- a/tests/unit/utils/test_geo.py
+++ b/tests/unit/utils/test_geo.py
@@ -166,6 +166,12 @@ def test_polygon_as_dict(self):
p = geo.Polygon([[[2.3, 57.32], [23.19, -20.2], [-120.4, 19.15], [2.3, 57.32]]])
self.assertEqual(expected, p.as_dict())
+ def test_polygon_points(self):
+ p = geo.Polygon([[[2.3, 57.32], [23.19, -20.2], [-120.4, 19.15], [2.3, 57.32]]])
+ result = p.points
+ self.assertTrue(result)
+ self.assertTrue(all([isinstance(p, geo.Point) for p in result]))
+
def test_polygon_from_points(self):
expected = geo.Polygon([[[2.3, 57.32], [23.19, -20.2], [2.3, 57.32]]])
list_of_lists = [
@@ -245,6 +251,7 @@ def test_from_polygons(self):
class TestGeometryBuilder(unittest.TestCase):
def test_unrecognized_geom_type(self):
+ self.assertRaises(AssertionError, geo.GeometryBuilder.build, None)
self.assertRaises(ValueError, geo.GeometryBuilder.build, {"type": "Unknown"})
self.assertRaises(ValueError, geo.GeometryBuilder.build, {})
diff --git a/tests/unit/utils/test_stringutils.py b/tests/unit/utils/test_stringutils.py
index 25302cbb..16cd0021 100644
--- a/tests/unit/utils/test_stringutils.py
+++ b/tests/unit/utils/test_stringutils.py
@@ -11,37 +11,3 @@ def test_obfuscate_API_key(self):
self.assertEqual(expected, stringutils.obfuscate_API_key(API_key))
self.assertIsNone(stringutils.obfuscate_API_key(None))
-
- def test_encode_to_utf8(self):
- name = 'testname'
- if sys.version_info > (3, 0):
- result = stringutils.encode_to_utf8(name)
- self.assertEqual(result, name)
- else: # Python 2
- result = stringutils.encode_to_utf8(name)
- try:
- result.decode('ascii')
- except:
- self.fail()
-
- def test_assert_is_string(self):
- a_string = 'test'
- a_non_string = 123
- stringutils.assert_is_string(a_string)
- self.assertRaises(AssertionError, stringutils.assert_is_string,
- a_non_string)
-
- def test_assert_is_string_or_unicode(self):
- a_string = 'test'
- a_non_string = 123
- stringutils.assert_is_string_or_unicode(a_string)
- self.assertRaises(AssertionError,
- stringutils.assert_is_string_or_unicode,
- a_non_string)
-
- try: # only for Python 2
- unicode_value = unicode('test')
- stringutils.assert_is_string_or_unicode(unicode_value)
- except:
- pass
-
diff --git a/tests/unit/utils/test_weatherutils.py b/tests/unit/utils/test_weatherutils.py
index f2ef4a48..0264c14e 100644
--- a/tests/unit/utils/test_weatherutils.py
+++ b/tests/unit/utils/test_weatherutils.py
@@ -3,9 +3,9 @@
"""
import unittest
-from pyowm.webapi25.weather import Weather
+from pyowm.weatherapi25.weather import Weather
from pyowm.utils import weatherutils
-from pyowm.webapi25.weathercoderegistry import WeatherCodeRegistry
+from pyowm.weatherapi25.weathercoderegistry import WeatherCodeRegistry
from pyowm.exceptions.api_response_error import NotFoundError
class TestWeatherUtils(unittest.TestCase):
diff --git a/tests/unit/uvindexapi30/test_uvindex.py b/tests/unit/uvindexapi30/test_uvindex.py
index 450cb845..9113985b 100644
--- a/tests/unit/uvindexapi30/test_uvindex.py
+++ b/tests/unit/uvindexapi30/test_uvindex.py
@@ -1,6 +1,6 @@
import unittest
from datetime import datetime
-from pyowm.webapi25.location import Location
+from pyowm.weatherapi25.location import Location
from pyowm.uvindexapi30.uvindex import UVIndex, uv_intensity_to_exposure_risk
from pyowm.utils.timeformatutils import UTC
diff --git a/tests/unit/weatherapi25/__init__.py b/tests/unit/weatherapi25/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/unit/webapi25/json_test_dumps.py b/tests/unit/weatherapi25/json_test_dumps.py
similarity index 100%
rename from tests/unit/webapi25/json_test_dumps.py
rename to tests/unit/weatherapi25/json_test_dumps.py
diff --git a/tests/unit/webapi25/json_test_responses.py b/tests/unit/weatherapi25/json_test_responses.py
similarity index 92%
rename from tests/unit/webapi25/json_test_responses.py
rename to tests/unit/weatherapi25/json_test_responses.py
index 1349c1fe..fa1d8586 100644
--- a/tests/unit/webapi25/json_test_responses.py
+++ b/tests/unit/weatherapi25/json_test_responses.py
@@ -26,6 +26,16 @@
',"dt":1419210000,"id":7343,"main":{"pressure":1007,"temp":0},"name":'\
'"UWSS","rang":50,"type":1,"wind":{"deg":180,"speed":3}}]}'
+WEATHER_AT_PLACES_IN_BBOX_JSON = '{"cod":"200","calctime":0.3107,"cnt":2,' \
+ '"list":[{"id":2208791,"name":"Yafran","coord":{"lon":12.52859,"lat":32.06329},"main":{"temp":9.68,"temp_min":9.681,' \
+ '"temp_max":9.681,"pressure":961.02,"sea_level":1036.82,"grnd_level":961.02,"humidity":85},"dt":1485784982,' \
+ '"wind":{"speed":3.96,"deg":356.5},"rain":{"3h":0.255},"clouds":{"all":88},"weather":[{"id":500,"main":"Rain",' \
+ '"description":"lightrain","icon":"10d"}]},{"id":2208425,"name":"Zuwarah","coord":{"lon":12.08199,"lat":32.931198},' \
+ '"main":{"temp":15.36,"temp_min":15.356,"temp_max":15.356,"pressure":1036.81,"sea_level":1037.79,' \
+ '"grnd_level":1036.81,"humidity":89},"dt":1485784982,"wind":{"speed":5.46,"deg":30.0002},"clouds":{"all":56},' \
+ '"weather":[{"id":803,"main":"Clouds","description":"brokenclouds","icon":"04d"}]}]}'
+
+
SEARCH_RESULTS_JSON = '{"cod": "200", "count": 2, "list": [{"clouds": {"all": ' \
'20}, "coord": {"lat": 51.50853, "lon": -0.12573999999999999}, "dt": 1378237178,' \
' "id": 2643743, "main": {"humidity": 56, "pressure": 1025, "temp": ' \
diff --git a/tests/unit/weatherapi25/parsers/__init__.py b/tests/unit/weatherapi25/parsers/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/unit/webapi25/parsers/test_forecastparser.py b/tests/unit/weatherapi25/parsers/test_forecastparser.py
similarity index 95%
rename from tests/unit/webapi25/parsers/test_forecastparser.py
rename to tests/unit/weatherapi25/parsers/test_forecastparser.py
index d36476ab..7c276c59 100644
--- a/tests/unit/webapi25/parsers/test_forecastparser.py
+++ b/tests/unit/weatherapi25/parsers/test_forecastparser.py
@@ -3,10 +3,10 @@
"""
import unittest
-from pyowm.webapi25.parsers.forecastparser import ForecastParser
+from pyowm.weatherapi25.parsers.forecastparser import ForecastParser
from pyowm.exceptions.parse_response_error import ParseResponseError
from pyowm.exceptions.api_response_error import APIResponseError
-from tests.unit.webapi25.json_test_responses import (
+from tests.unit.weatherapi25.json_test_responses import (
THREE_HOURS_FORECAST_JSON, FORECAST_NOT_FOUND_JSON,
INTERNAL_SERVER_ERROR_JSON, FORECAST_MALFORMED_JSON)
diff --git a/tests/unit/webapi25/parsers/test_observationlistparser.py b/tests/unit/weatherapi25/parsers/test_observationlistparser.py
similarity index 94%
rename from tests/unit/webapi25/parsers/test_observationlistparser.py
rename to tests/unit/weatherapi25/parsers/test_observationlistparser.py
index 95080c4a..bbc47648 100644
--- a/tests/unit/webapi25/parsers/test_observationlistparser.py
+++ b/tests/unit/weatherapi25/parsers/test_observationlistparser.py
@@ -2,10 +2,10 @@
Test case for observationlistparser.py module
"""
import unittest
-from pyowm.webapi25.parsers.observationlistparser import ObservationListParser
+from pyowm.weatherapi25.parsers.observationlistparser import ObservationListParser
from pyowm.exceptions.parse_response_error import ParseResponseError
from pyowm.exceptions.api_response_error import APIResponseError
-from tests.unit.webapi25.json_test_responses import (
+from tests.unit.weatherapi25.json_test_responses import (
SEARCH_RESULTS_JSON, SEARCH_WITH_NO_RESULTS_JSON,
INTERNAL_SERVER_ERROR_JSON)
diff --git a/tests/unit/webapi25/parsers/test_observationparser.py b/tests/unit/weatherapi25/parsers/test_observationparser.py
similarity index 92%
rename from tests/unit/webapi25/parsers/test_observationparser.py
rename to tests/unit/weatherapi25/parsers/test_observationparser.py
index c1bad99f..a3939059 100644
--- a/tests/unit/webapi25/parsers/test_observationparser.py
+++ b/tests/unit/weatherapi25/parsers/test_observationparser.py
@@ -2,10 +2,10 @@
Test case for observationparser.py module
"""
import unittest
-from pyowm.webapi25.parsers.observationparser import ObservationParser
+from pyowm.weatherapi25.parsers.observationparser import ObservationParser
from pyowm.exceptions.parse_response_error import ParseResponseError
from pyowm.exceptions.api_response_error import APIResponseError
-from tests.unit.webapi25.json_test_responses import (
+from tests.unit.weatherapi25.json_test_responses import (
OBSERVATION_JSON, OBSERVATION_NOT_FOUND_JSON, OBSERVATION_MALFORMED_JSON)
diff --git a/tests/unit/webapi25/parsers/test_stationhistoryparser.py b/tests/unit/weatherapi25/parsers/test_stationhistoryparser.py
similarity index 90%
rename from tests/unit/webapi25/parsers/test_stationhistoryparser.py
rename to tests/unit/weatherapi25/parsers/test_stationhistoryparser.py
index 258c4332..6b8fd30a 100644
--- a/tests/unit/webapi25/parsers/test_stationhistoryparser.py
+++ b/tests/unit/weatherapi25/parsers/test_stationhistoryparser.py
@@ -2,11 +2,11 @@
Test case for stationhistoryparser.py module
"""
import unittest
-from pyowm.webapi25.parsers.stationhistoryparser import StationHistoryParser
-from pyowm.webapi25.stationhistory import StationHistory
+from pyowm.weatherapi25.parsers.stationhistoryparser import StationHistoryParser
+from pyowm.weatherapi25.stationhistory import StationHistory
from pyowm.exceptions.parse_response_error import ParseResponseError
from pyowm.exceptions.api_response_error import APIResponseError
-from tests.unit.webapi25.json_test_responses import (
+from tests.unit.weatherapi25.json_test_responses import (
STATION_TICK_WEATHER_HISTORY_JSON, STATION_WEATHER_HISTORY_NOT_FOUND_JSON,
INTERNAL_SERVER_ERROR_JSON)
diff --git a/tests/unit/webapi25/parsers/test_weatherhistoryparser.py b/tests/unit/weatherapi25/parsers/test_weatherhistoryparser.py
similarity index 83%
rename from tests/unit/webapi25/parsers/test_weatherhistoryparser.py
rename to tests/unit/weatherapi25/parsers/test_weatherhistoryparser.py
index 77921708..aba8f1d7 100644
--- a/tests/unit/webapi25/parsers/test_weatherhistoryparser.py
+++ b/tests/unit/weatherapi25/parsers/test_weatherhistoryparser.py
@@ -2,12 +2,12 @@
Test case for weatherhistoryparser.py module
"""
import unittest
-from pyowm.webapi25.parsers.weatherhistoryparser import WeatherHistoryParser
+from pyowm.weatherapi25.parsers.weatherhistoryparser import WeatherHistoryParser
from pyowm.exceptions.parse_response_error import ParseResponseError
from pyowm.exceptions.api_response_error import APIResponseError
-from tests.unit.webapi25.json_test_responses import (CITY_WEATHER_HISTORY_JSON,
- CITY_WEATHER_HISTORY_NO_RESULTS_JSON, CITY_WEATHER_HISTORY_NOT_FOUND_JSON,
- INTERNAL_SERVER_ERROR_JSON)
+from tests.unit.weatherapi25.json_test_responses import (CITY_WEATHER_HISTORY_JSON,
+ CITY_WEATHER_HISTORY_NO_RESULTS_JSON, CITY_WEATHER_HISTORY_NOT_FOUND_JSON,
+ INTERNAL_SERVER_ERROR_JSON)
class TestWeatherHistoryParser(unittest.TestCase):
diff --git a/tests/unit/webapi25/test_cityidregistry.py b/tests/unit/weatherapi25/test_cityidregistry.py
similarity index 99%
rename from tests/unit/webapi25/test_cityidregistry.py
rename to tests/unit/weatherapi25/test_cityidregistry.py
index 43aa2fde..09f3e7e5 100644
--- a/tests/unit/webapi25/test_cityidregistry.py
+++ b/tests/unit/weatherapi25/test_cityidregistry.py
@@ -7,8 +7,8 @@
from StringIO import StringIO
except ImportError:
from io import StringIO
-from pyowm.webapi25.cityidregistry import CityIDRegistry
-from pyowm.webapi25.location import Location
+from pyowm.weatherapi25.cityidregistry import CityIDRegistry
+from pyowm.weatherapi25.location import Location
from pyowm.utils.geo import Point
diff --git a/tests/unit/webapi25/test_forecast.py b/tests/unit/weatherapi25/test_forecast.py
similarity index 95%
rename from tests/unit/webapi25/test_forecast.py
rename to tests/unit/weatherapi25/test_forecast.py
index 127fcc5d..adbfaef9 100644
--- a/tests/unit/webapi25/test_forecast.py
+++ b/tests/unit/weatherapi25/test_forecast.py
@@ -4,12 +4,12 @@
import unittest
from datetime import datetime
-from pyowm.webapi25.location import Location
-from pyowm.webapi25.weather import Weather
-from pyowm.webapi25.forecast import Forecast
+from pyowm.weatherapi25.location import Location
+from pyowm.weatherapi25.weather import Weather
+from pyowm.weatherapi25.forecast import Forecast
from pyowm.utils.timeformatutils import UTC
-from tests.unit.webapi25.json_test_dumps import FORECAST_JSON_DUMP
-from tests.unit.webapi25.xml_test_dumps import FORECAST_XML_DUMP
+from tests.unit.weatherapi25.json_test_dumps import FORECAST_JSON_DUMP
+from tests.unit.weatherapi25.xml_test_dumps import FORECAST_XML_DUMP
class TestForecast(unittest.TestCase):
diff --git a/tests/unit/webapi25/test_forecaster.py b/tests/unit/weatherapi25/test_forecaster.py
similarity index 98%
rename from tests/unit/webapi25/test_forecaster.py
rename to tests/unit/weatherapi25/test_forecaster.py
index 1bedf7de..1fff96c7 100644
--- a/tests/unit/webapi25/test_forecaster.py
+++ b/tests/unit/weatherapi25/test_forecaster.py
@@ -4,10 +4,10 @@
import unittest
from datetime import datetime
-from pyowm.webapi25.location import Location
-from pyowm.webapi25.weather import Weather
-from pyowm.webapi25.forecast import Forecast
-from pyowm.webapi25.forecaster import Forecaster
+from pyowm.weatherapi25.location import Location
+from pyowm.weatherapi25.weather import Weather
+from pyowm.weatherapi25.forecast import Forecast
+from pyowm.weatherapi25.forecaster import Forecaster
from pyowm.utils.timeformatutils import UTC
diff --git a/tests/unit/webapi25/test_historian.py b/tests/unit/weatherapi25/test_historian.py
similarity index 98%
rename from tests/unit/webapi25/test_historian.py
rename to tests/unit/weatherapi25/test_historian.py
index 15b1e6a8..0017d8a7 100644
--- a/tests/unit/webapi25/test_historian.py
+++ b/tests/unit/weatherapi25/test_historian.py
@@ -3,8 +3,8 @@
"""
import unittest
-from pyowm.webapi25.stationhistory import StationHistory
-from pyowm.webapi25.historian import Historian
+from pyowm.weatherapi25.stationhistory import StationHistory
+from pyowm.weatherapi25.historian import Historian
from pyowm.utils import temputils
diff --git a/tests/unit/webapi25/test_location.py b/tests/unit/weatherapi25/test_location.py
similarity index 96%
rename from tests/unit/webapi25/test_location.py
rename to tests/unit/weatherapi25/test_location.py
index 022f907f..0d2c8921 100644
--- a/tests/unit/webapi25/test_location.py
+++ b/tests/unit/weatherapi25/test_location.py
@@ -4,10 +4,10 @@
import unittest
import json
-from pyowm.webapi25.location import Location, location_from_dictionary
+from pyowm.weatherapi25.location import Location, location_from_dictionary
from pyowm.utils.geo import Point
-from tests.unit.webapi25.json_test_dumps import LOCATION_JSON_DUMP
-from tests.unit.webapi25.xml_test_dumps import LOCATION_XML_DUMP
+from tests.unit.weatherapi25.json_test_dumps import LOCATION_JSON_DUMP
+from tests.unit.weatherapi25.xml_test_dumps import LOCATION_XML_DUMP
class TestLocation(unittest.TestCase):
diff --git a/tests/unit/webapi25/test_observation.py b/tests/unit/weatherapi25/test_observation.py
similarity index 90%
rename from tests/unit/webapi25/test_observation.py
rename to tests/unit/weatherapi25/test_observation.py
index db8cea77..549ffaa1 100644
--- a/tests/unit/webapi25/test_observation.py
+++ b/tests/unit/weatherapi25/test_observation.py
@@ -4,12 +4,12 @@
import unittest
from datetime import datetime
-from pyowm.webapi25.location import Location
-from pyowm.webapi25.weather import Weather
-from pyowm.webapi25.observation import Observation
+from pyowm.weatherapi25.location import Location
+from pyowm.weatherapi25.weather import Weather
+from pyowm.weatherapi25.observation import Observation
from pyowm.utils.timeformatutils import UTC
-from tests.unit.webapi25.json_test_dumps import OBSERVATION_JSON_DUMP
-from tests.unit.webapi25.xml_test_dumps import OBSERVATION_XML_DUMP
+from tests.unit.weatherapi25.json_test_dumps import OBSERVATION_JSON_DUMP
+from tests.unit.weatherapi25.xml_test_dumps import OBSERVATION_XML_DUMP
class TestObservation(unittest.TestCase):
diff --git a/tests/unit/webapi25/test_owm25.py b/tests/unit/weatherapi25/test_owm25.py
similarity index 93%
rename from tests/unit/webapi25/test_owm25.py
rename to tests/unit/weatherapi25/test_owm25.py
index 468c27b3..8a195685 100644
--- a/tests/unit/webapi25/test_owm25.py
+++ b/tests/unit/weatherapi25/test_owm25.py
@@ -14,44 +14,44 @@
import unittest
import time
-from tests.unit.webapi25.json_test_responses import (OBSERVATION_JSON,
- SEARCH_RESULTS_JSON, THREE_HOURS_FORECAST_JSON, DAILY_FORECAST_JSON,
- THREE_HOURS_FORECAST_AT_COORDS_JSON, DAILY_FORECAST_AT_COORDS_JSON,
- THREE_HOURS_FORECAST_AT_ID_JSON, DAILY_FORECAST_AT_ID_JSON,
- CITY_WEATHER_HISTORY_JSON, STATION_TICK_WEATHER_HISTORY_JSON,
- STATION_WEATHER_HISTORY_JSON, THREE_HOURS_FORECAST_NOT_FOUND_JSON,
- DAILY_FORECAST_NOT_FOUND_JSON, STATION_HISTORY_NO_ITEMS_JSON,
- STATION_OBSERVATION_JSON, STATION_AT_COORDS_JSON,
- WEATHER_AT_STATION_IN_BBOX_JSON)
+from tests.unit.weatherapi25.json_test_responses import (OBSERVATION_JSON,
+ SEARCH_RESULTS_JSON, THREE_HOURS_FORECAST_JSON, DAILY_FORECAST_JSON,
+ THREE_HOURS_FORECAST_AT_COORDS_JSON, DAILY_FORECAST_AT_COORDS_JSON,
+ THREE_HOURS_FORECAST_AT_ID_JSON, DAILY_FORECAST_AT_ID_JSON,
+ CITY_WEATHER_HISTORY_JSON, STATION_TICK_WEATHER_HISTORY_JSON,
+ STATION_WEATHER_HISTORY_JSON, THREE_HOURS_FORECAST_NOT_FOUND_JSON,
+ DAILY_FORECAST_NOT_FOUND_JSON, STATION_HISTORY_NO_ITEMS_JSON,
+ STATION_OBSERVATION_JSON, STATION_AT_COORDS_JSON,
+ WEATHER_AT_STATION_IN_BBOX_JSON, WEATHER_AT_PLACES_IN_BBOX_JSON)
from tests.unit.uvindexapi30.test_uvindexparser import UVINDEX_JSON
from tests.unit.uvindexapi30.test_uvindexlistparser import UVINDEX_LIST_JSON
from tests.unit.pollutionapi30.test_parsers import COINDEX_JSON, OZONE_JSON, NO2INDEX_JSON, SO2INDEX_JSON
-from pyowm.webapi25.owm25 import OWM25
+from pyowm.weatherapi25.owm25 import OWM25
from pyowm.constants import PYOWM_VERSION
from pyowm.commons.http_client import HttpClient
from pyowm.uvindexapi30.uv_client import UltraVioletHttpClient
from pyowm.pollutionapi30.airpollution_client import AirPollutionHttpClient
from pyowm.exceptions.api_call_error import APICallTimeoutError
-from pyowm.webapi25.forecast import Forecast
-from pyowm.webapi25.observation import Observation
-from pyowm.webapi25.weather import Weather
-from pyowm.webapi25.location import Location
-from pyowm.webapi25.forecaster import Forecaster
-from pyowm.webapi25.station import Station
-from pyowm.webapi25.stationhistory import StationHistory
-from pyowm.webapi25.historian import Historian
+from pyowm.weatherapi25.forecast import Forecast
+from pyowm.weatherapi25.observation import Observation
+from pyowm.weatherapi25.weather import Weather
+from pyowm.weatherapi25.location import Location
+from pyowm.weatherapi25.forecaster import Forecaster
+from pyowm.weatherapi25.station import Station
+from pyowm.weatherapi25.stationhistory import StationHistory
+from pyowm.weatherapi25.historian import Historian
from pyowm.uvindexapi30.uvindex import UVIndex
from pyowm.pollutionapi30.coindex import COIndex
from pyowm.pollutionapi30.ozone import Ozone
from pyowm.pollutionapi30.no2index import NO2Index
from pyowm.pollutionapi30.so2index import SO2Index
-from pyowm.webapi25.parsers.forecastparser import ForecastParser
-from pyowm.webapi25.parsers.observationparser import ObservationParser
-from pyowm.webapi25.parsers.observationlistparser import ObservationListParser
-from pyowm.webapi25.parsers.stationparser import StationParser
-from pyowm.webapi25.parsers.stationlistparser import StationListParser
-from pyowm.webapi25.parsers.stationhistoryparser import StationHistoryParser
-from pyowm.webapi25.parsers.weatherhistoryparser import WeatherHistoryParser
+from pyowm.weatherapi25.parsers.forecastparser import ForecastParser
+from pyowm.weatherapi25.parsers.observationparser import ObservationParser
+from pyowm.weatherapi25.parsers.observationlistparser import ObservationListParser
+from pyowm.weatherapi25.parsers.stationparser import StationParser
+from pyowm.weatherapi25.parsers.stationlistparser import StationListParser
+from pyowm.weatherapi25.parsers.stationhistoryparser import StationHistoryParser
+from pyowm.weatherapi25.parsers.weatherhistoryparser import WeatherHistoryParser
from pyowm.uvindexapi30.parsers import UVIndexParser, UVIndexListParser
from pyowm.pollutionapi30.parsers import COIndexParser, NO2IndexParser, SO2IndexParser, OzoneParser
from pyowm.stationsapi30.stations_manager import StationsManager
@@ -135,6 +135,9 @@ def mock_call_api_returning_station_history_with_no_items(self, uri, params=None
def mock_api_call_returning_weather_at_stations_in_bbox(self, uri, params=None, headers=None):
return 200, WEATHER_AT_STATION_IN_BBOX_JSON
+ def mock_api_call_returning_weather_at_places_in_bbox(self, uri, params=None, headers=None):
+ return 200, WEATHER_AT_PLACES_IN_BBOX_JSON
+
def mock_api_call_returning_station_at_coords(self, uri, params=None, headers=None):
return 200, STATION_AT_COORDS_JSON
@@ -742,6 +745,29 @@ def test_weather_at_station_in_bbox(self):
self.assertTrue(isinstance(result.get_location(), Location))
self.assertTrue(result.get_reception_time() is not None)
+ def test_weather_at_places_in_bbox_fails_with_wrong_params(self):
+ self.assertRaises(AssertionError, OWM25.weather_at_places_in_bbox,
+ self.__test_instance, 12, 32, 15, 37, 'zoom')
+ self.assertRaises(ValueError, OWM25.weather_at_places_in_bbox,
+ self.__test_instance, 12, 32, 15, 37, -30)
+ self.assertRaises(AssertionError, OWM25.weather_at_places_in_bbox,
+ self.__test_instance, 12, 32, 15, 37, 10, 'cluster')
+
+ def test_weather_at_places_in_bbox(self):
+ original_func = HttpClient.cacheable_get_json
+ HttpClient.cacheable_get_json = \
+ self.mock_api_call_returning_weather_at_places_in_bbox
+ results = self.__test_instance\
+ .weather_at_places_in_bbox(12,32,15,37,10)
+ HttpClient.cacheable_get_json = original_func
+ self.assertTrue(isinstance(results, list))
+ for result in results:
+ self.assertTrue(isinstance(result, Observation))
+ self.assertTrue(isinstance(result.get_weather(), Weather))
+ self.assertTrue(isinstance(result.get_location(), Location))
+ self.assertTrue(result.get_reception_time() is not None)
+
+
def test_station_tick_history(self):
original_func = HttpClient.cacheable_get_json
HttpClient.cacheable_get_json = \
diff --git a/tests/unit/webapi25/test_station.py b/tests/unit/weatherapi25/test_station.py
similarity index 92%
rename from tests/unit/webapi25/test_station.py
rename to tests/unit/weatherapi25/test_station.py
index 7136ad22..9d3b9b7c 100644
--- a/tests/unit/webapi25/test_station.py
+++ b/tests/unit/weatherapi25/test_station.py
@@ -4,10 +4,10 @@
import unittest
-from pyowm.webapi25.station import Station
-from pyowm.webapi25.weather import Weather
-from tests.unit.webapi25.json_test_dumps import STATION_JSON_DUMP
-from tests.unit.webapi25.xml_test_dumps import STATION_XML_DUMP
+from pyowm.weatherapi25.station import Station
+from pyowm.weatherapi25.weather import Weather
+from tests.unit.weatherapi25.json_test_dumps import STATION_JSON_DUMP
+from tests.unit.weatherapi25.xml_test_dumps import STATION_XML_DUMP
class TestStation(unittest.TestCase):
diff --git a/tests/unit/webapi25/test_stationhistory.py b/tests/unit/weatherapi25/test_stationhistory.py
similarity index 93%
rename from tests/unit/webapi25/test_stationhistory.py
rename to tests/unit/weatherapi25/test_stationhistory.py
index 56b459b6..706d1cc3 100644
--- a/tests/unit/webapi25/test_stationhistory.py
+++ b/tests/unit/weatherapi25/test_stationhistory.py
@@ -4,10 +4,10 @@
import unittest
from datetime import datetime
-from pyowm.webapi25.stationhistory import StationHistory
+from pyowm.weatherapi25.stationhistory import StationHistory
from pyowm.utils.timeformatutils import UTC
-from tests.unit.webapi25.json_test_dumps import STATIONHISTORY_JSON_DUMP
-from tests.unit.webapi25.xml_test_dumps import STATIONHISTORY_XML_DUMP
+from tests.unit.weatherapi25.json_test_dumps import STATIONHISTORY_JSON_DUMP
+from tests.unit.weatherapi25.xml_test_dumps import STATIONHISTORY_XML_DUMP
class TestStationHistory(unittest.TestCase):
diff --git a/tests/unit/webapi25/test_weather.py b/tests/unit/weatherapi25/test_weather.py
similarity index 99%
rename from tests/unit/webapi25/test_weather.py
rename to tests/unit/weatherapi25/test_weather.py
index 9bb63193..d7e02b4e 100644
--- a/tests/unit/webapi25/test_weather.py
+++ b/tests/unit/weatherapi25/test_weather.py
@@ -3,10 +3,9 @@
"""
import unittest
-from pyowm.webapi25.weather import Weather, weather_from_dictionary
+from pyowm.weatherapi25.weather import Weather, weather_from_dictionary
from pyowm.utils.timeformatutils import UTC
-from tests.unit.webapi25.json_test_dumps import WEATHER_JSON_DUMP
-from tests.unit.webapi25.xml_test_dumps import WEATHER_XML_DUMP
+from tests.unit.weatherapi25.json_test_dumps import WEATHER_JSON_DUMP
from datetime import datetime
diff --git a/tests/unit/webapi25/test_weathercoderegistry.py b/tests/unit/weatherapi25/test_weathercoderegistry.py
similarity index 89%
rename from tests/unit/webapi25/test_weathercoderegistry.py
rename to tests/unit/weatherapi25/test_weathercoderegistry.py
index e93963e3..6a8eeeaf 100644
--- a/tests/unit/webapi25/test_weathercoderegistry.py
+++ b/tests/unit/weatherapi25/test_weathercoderegistry.py
@@ -3,7 +3,7 @@
"""
import unittest
-from pyowm.webapi25.weathercoderegistry import WeatherCodeRegistry
+from pyowm.weatherapi25.weathercoderegistry import WeatherCodeRegistry
class TestWeatherCodeRegistry(unittest.TestCase):
diff --git a/tests/unit/weatherapi25/xml_test_dumps.py b/tests/unit/weatherapi25/xml_test_dumps.py
new file mode 100644
index 00000000..376f4b5d
--- /dev/null
+++ b/tests/unit/weatherapi25/xml_test_dumps.py
@@ -0,0 +1,22 @@
+"""
+XML dumps for PyOWM test objects
+"""
+
+LOCATION_XML_DUMP = """
+London12.343.71234UK"""
+
+WEATHER_XML_DUMP = """
+Clouds8042001030.1191038.589137844960004d67-1.899294.199294.199296.098Overcast clouds13784592001378496400571.1252.0022.091000300.0298.040.0"""
+
+OBSERVATION_XML_DUMP = """
+1234567test12.343.7987UKClouds8042001030.1191038.589137844960004d67-1.899294.199294.199296.098Overcast clouds13784592001378496400571.1252.0021000300.0298.0296.0"""
+
+FORECAST_XML_DUMP = """
+daily1234567test12.343.7987ITClouds8042001030.1191038.589137844960004d67-1.899294.199294.199296.098Overcast clouds13784592001378496400571.1252.0021000300.0298.0296.0Clear8041001070.1191078.589137844951002d23-1.899295.6297.199299.0Sky is clear13784596901378496480124.2103.41000300.0298.0296.0"""
+
+STATIONHISTORY_XML_DUMP = """
+2865tick1378684800266.85136293404327.71010.094.7266.25136293398327.31010.024.7"""
+
+
+STATION_XML_DUMP = """
+KNGU286515036.9375-76.289318.95Clouds8042001030.1191038.589137844960004d67-1.899294.199294.199296.098Overcast clouds13784592001378496400571.1252.0021000300.0298.0296.0"""
diff --git a/tests/unit/webapi25/xml_test_dumps.py b/tests/unit/webapi25/xml_test_dumps.py
deleted file mode 100644
index 0575a4ac..00000000
--- a/tests/unit/webapi25/xml_test_dumps.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""
-XML dumps for PyOWM test objects
-"""
-
-LOCATION_XML_DUMP = """
-London12.343.71234UK"""
-
-WEATHER_XML_DUMP = """
-Clouds8042001030.1191038.589137844960004d67-1.899294.199294.199296.098Overcast clouds13784592001378496400571.1252.0022.091000300.0298.040.0"""
-
-OBSERVATION_XML_DUMP = """
-1234567test12.343.7987UKClouds8042001030.1191038.589137844960004d67-1.899294.199294.199296.098Overcast clouds13784592001378496400571.1252.0021000300.0298.0296.0"""
-
-FORECAST_XML_DUMP = """
-daily1234567test12.343.7987ITClouds8042001030.1191038.589137844960004d67-1.899294.199294.199296.098Overcast clouds13784592001378496400571.1252.0021000300.0298.0296.0Clear8041001070.1191078.589137844951002d23-1.899295.6297.199299.0Sky is clear13784596901378496480124.2103.41000300.0298.0296.0"""
-
-STATIONHISTORY_XML_DUMP = """
-2865tick1378684800266.85136293404327.71010.094.7266.25136293398327.31010.024.7"""
-
-
-STATION_XML_DUMP = """
-KNGU286515036.9375-76.289318.95Clouds8042001030.1191038.589137844960004d67-1.899294.199294.199296.098Overcast clouds13784592001378496400571.1252.0021000300.0298.0296.0"""
diff --git a/tox.ini b/tox.ini
index fbd17d71..5f3f083a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,17 +1,16 @@
[tox]
envlist =
- py27, py34, py35, py36, py37
+ py34, py35, py36, py37, coverage
skip_missing_interpreters =
True
[testenv]
commands =
- pip install -r requirements.txt
python setup.py test -s tests.unit
-[testenv:docs]
-changedir = sphinx
-deps =
- sphinx
+[testenv:coverage]
+whitelist_externals = coverage
commands =
- make html
+ coverage run --rcfile=.coveragerc setup.py test -s tests.unit
+ coverage html
+ coverage report