1# -*- coding: utf-8 -*-
2
3# Copyright(C) 2012 Romain Bignon
4#
5# This file is part of weboob.
6#
7# weboob is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Lesser General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# weboob is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Lesser General Public License for more details.
16#
17# You should have received a copy of the GNU Lesser General Public License
18# along with weboob. If not, see <http://www.gnu.org/licenses/>.
19
20from __future__ import print_function
21
22from weboob.capabilities.housing import (CapHousing, Query, POSTS_TYPES,
23                                         ADVERT_TYPES, HOUSE_TYPES)
24from weboob.capabilities.base import empty
25from weboob.tools.application.repl import ReplApplication, defaultcount
26from weboob.tools.application.formatters.iformatter import IFormatter, PrettyFormatter
27from weboob.tools.config.yamlconfig import YamlConfig
28
29
30__all__ = ['Flatboob']
31
32
33class HousingFormatter(IFormatter):
34    MANDATORY_FIELDS = ('id', 'title', 'cost', 'currency', 'area', 'date', 'text')
35
36    def format_obj(self, obj, alias):
37        result = u'%s%s%s\n' % (self.BOLD, obj.title, self.NC)
38        result += 'ID: %s\n' % obj.fullid
39
40        if hasattr(obj, 'url') and obj.url:
41            result += 'URL: %s\n' % obj.url
42
43        result += 'Cost: %s%s %s\n' % (obj.cost, obj.currency, obj.utilities)
44        area = u'%.2fm²' % (obj.area) if obj.area else u'%s' % obj.area
45        result += u'Area: %s\n' % area
46        if hasattr(obj, 'price_per_meter') and not empty(obj.price_per_meter):
47            result += u'Price per square meter: %.2f %s/m²\n' % (obj.price_per_meter, obj.currency)
48        if hasattr(obj, 'rooms') and not empty(obj.rooms):
49            result += u'Rooms: %d\n' % (obj.rooms)
50        if hasattr(obj, 'bedrooms') and not empty(obj.bedrooms):
51            result += u'Bedrooms: %d\n' % (obj.bedrooms)
52        if obj.date:
53            result += 'Date: %s\n' % obj.date.strftime('%Y-%m-%d')
54        result += 'Phone: %s\n' % obj.phone
55        if hasattr(obj, 'location') and obj.location:
56            result += 'Location: %s\n' % obj.location
57        if hasattr(obj, 'station') and obj.station:
58            result += 'Station: %s\n' % obj.station
59
60        if hasattr(obj, 'photos') and obj.photos:
61            result += '\n%sPhotos%s\n' % (self.BOLD, self.NC)
62            for photo in obj.photos:
63                result += ' * %s\n' % photo.url
64
65        result += '\n%sDescription%s\n' % (self.BOLD, self.NC)
66        result += obj.text
67
68        if hasattr(obj, 'details') and obj.details:
69            result += '\n\n%sDetails%s\n' % (self.BOLD, self.NC)
70            for key, value in obj.details.items():
71                result += ' %s: %s\n' % (key, value)
72
73        return result
74
75
76class HousingListFormatter(PrettyFormatter):
77    MANDATORY_FIELDS = ('id', 'title', 'cost', 'text')
78
79    def get_title(self, obj):
80        return '%s%s %s - %s' % (obj.cost, obj.currency, obj.utilities, obj.title)
81
82    def get_description(self, obj):
83        result = u''
84        if hasattr(obj, 'date') and obj.date:
85            result += '%s - ' % obj.date.strftime('%Y-%m-%d')
86        result += obj.text
87        return result
88
89
90class Flatboob(ReplApplication):
91    APPNAME = 'flatboob'
92    VERSION = '2.0'
93    COPYRIGHT = 'Copyright(C) 2012-YEAR Romain Bignon'
94    DESCRIPTION = "Console application to search for housing."
95    SHORT_DESCRIPTION = "search for housing"
96    CAPS = CapHousing
97    CONFIG = {'queries': {}}
98    EXTRA_FORMATTERS = {'housing_list': HousingListFormatter,
99                        'housing':      HousingFormatter,
100                       }
101    COMMANDS_FORMATTERS = {'search': 'housing_list',
102                           'info': 'housing',
103                           'load': 'housing_list'
104                          }
105
106    def main(self, argv):
107        self.load_config(klass=YamlConfig)
108        return ReplApplication.main(self, argv)
109
110    @defaultcount(10)
111    def do_search(self, line):
112        """
113        search
114
115        Search for housing. Parameters are interactively asked.
116        """
117        pattern = 'notempty'
118        query = Query()
119        query.cities = []
120        while pattern:
121            if len(query.cities) > 0:
122                print('\n%sSelected cities:%s %s' % (self.BOLD, self.NC, ', '.join([c.name for c in query.cities])))
123            pattern = self.ask('Enter a city pattern (or empty to stop)', default='')
124            if not pattern:
125                break
126
127            cities = []
128            for city in self.weboob.do('search_city', pattern):
129                cities.append(city)
130
131            if len(cities) == 0:
132                print('  Not found!')
133                continue
134            if len(cities) == 1:
135                if city in query.cities:
136                    query.cities.remove(city)
137                else:
138                    query.cities.append(city)
139                continue
140
141            r = 'notempty'
142            while r != '':
143                for i, city in enumerate(cities):
144                    print('  %s%2d)%s [%s] %s (%s)' % (self.BOLD, i+1, self.NC, 'x' if city in query.cities else ' ', city.name, city.backend))
145                r = self.ask('Select cities (or empty to stop)', regexp='(\d+|)', default='')
146                if not r.isdigit():
147                    continue
148                r = int(r)
149                if r <= 0 or r > len(cities):
150                    continue
151                city = cities[r-1]
152                if city in query.cities:
153                    query.cities.remove(city)
154                else:
155                    query.cities.append(city)
156
157        r = 'notempty'
158        while r != '':
159            for i, good in enumerate(HOUSE_TYPES, 1):
160                print('  %s%2d)%s [%s] %s' % (self.BOLD,
161                                              i,
162                                              self.NC,
163                                              'x' if good in query.house_types else ' ', good))
164            r = self.ask('Select type of house (or empty to stop)', regexp='(\d+|)', default='')
165            if not r.isdigit():
166                continue
167            r = int(r)
168            if r <= 0 or r > len(HOUSE_TYPES):
169                continue
170            value = list(HOUSE_TYPES)[r - 1]
171            if value in query.house_types:
172                query.house_types.remove(value)
173            else:
174                query.house_types.append(value)
175
176        _type = None
177        posts_types = sorted(POSTS_TYPES)
178        while _type not in range(len(posts_types)):
179            for i, t in enumerate(posts_types):
180                print('  %s%2d)%s %s' % (self.BOLD,
181                                         i,
182                                         self.NC,
183                                         t))
184            _type = self.ask_int('Type of query')
185
186        query.type = posts_types[_type]
187
188        r = 'notempty'
189        while r != '':
190            for i, good in enumerate(ADVERT_TYPES, 1):
191                print('  %s%2d)%s [%s] %s' % (self.BOLD,
192                                              i,
193                                              self.NC,
194                                              'x' if good in query.advert_types else ' ', good))
195            r = self.ask('Select type of posts (or empty to stop)', regexp='(\d+|)', default='')
196            if not r.isdigit():
197                continue
198            r = int(r)
199            if r <= 0 or r > len(ADVERT_TYPES):
200                continue
201            value = list(ADVERT_TYPES)[r - 1]
202            if value in query.advert_types:
203                query.advert_types.remove(value)
204            else:
205                query.advert_types.append(value)
206
207        query.area_min = self.ask_int('Enter min area')
208        query.area_max = self.ask_int('Enter max area')
209        query.cost_min = self.ask_int('Enter min cost')
210        query.cost_max = self.ask_int('Enter max cost')
211        query.nb_rooms = self.ask_int('Enter number of rooms')
212        save_query = self.ask('Save query (y/n)', default='n')
213        if save_query.upper() == 'Y':
214            name = ''
215            while not name:
216                name = self.ask('Query name')
217
218            self.config.set('queries', name, query)
219            self.config.save()
220        self.complete_search(query)
221
222    def complete_search(self, query):
223        self.change_path([u'housings'])
224        self.start_format()
225        for housing in self.do('search_housings', query):
226            self.cached_format(housing)
227
228    def ask_int(self, txt):
229        r = self.ask(txt, default='', regexp='(\d+|)')
230        if r:
231            return int(r)
232        return None
233
234    @defaultcount(10)
235    def do_load(self, query_name):
236        """
237        load [query name]
238        without query name : list loadable queries
239        with query name laod query
240        """
241        queries = self.config.get('queries')
242        if not queries:
243            print('There is no saved queries', file=self.stderr)
244            return 2
245
246        if not query_name:
247            for name in queries.keys():
248                print('  %s* %s %s' % (self.BOLD,
249                                       self.NC,
250                                       name))
251            query_name = self.ask('Which one')
252
253        if query_name in queries:
254            self.complete_search(queries.get(query_name))
255        else:
256            print('Unknown query', file=self.stderr)
257            return 2
258
259    def complete_info(self, text, line, *ignored):
260        args = line.split(' ')
261        if len(args) == 2:
262            return self._complete_object()
263
264    def do_info(self, _id):
265        """
266        info ID
267
268        Get information about a housing.
269        """
270        if not _id:
271            print('This command takes an argument: %s' % self.get_command_help('info', short=True), file=self.stderr)
272            return 2
273
274        housing = self.get_object(_id, 'get_housing')
275        if not housing:
276            print('Housing not found: %s' % _id, file=self.stderr)
277            return 3
278
279        self.start_format()
280        self.format(housing)
281