Code Sample: Python

Running on a custom WSGI server, this file contains the views for the Model-View-Controller architecture of the web interface of my personal weather station. The views can display observations, tables, graphics, plots, and much more for today's weather.

1#!/usr/bin/env python3

2from database.config import DatabaseConnection

3from database.settings import DatabaseSettings

4from weather.api import WundergroundApiJson

5from io import StringIO, BytesIO

6from PIL import Image

7import weather.units as units

8import weather.weather as wx

9import weather.settings as stg

10import weather.keys as key

11import wsgi.models as models

12import wsgi.utilities as util

13import csv

14import json

15import os

16import datetime

17import matplotlib.pyplot as plt

18

19DATABASE_SETTINGS = DatabaseSettings()

20

21class WebView(object):

22    

23    ICON_URL_TEMPLATE = 'http://icons.wxug.com/i/c/k/{}'

24    

25    def __init__(self, api_json):

26        """

27        Loads data into the View

28        

29        Args:

30            api_json (object): A WundergroundApiJson() object.

31        """

32        self.__api_json = api_json

33    

34    @property

35    def api_json(self):

36        return self.__api_json

37    

38    @property

39    def html(self):

40        template = util.load_template(self.TEMPLATE)

41        html = template.format(**vars())

42        return html

43    

44    def to_imperial(self):

45        pass

46    

47    def to_metric(self):

48        pass

49

50

51class DownloadableFileView(WebView):

52    

53    def __init__(self, iso=None, metric=DATABASE_SETTINGS.metric, meteogram=False, **kwargs):

54        """

55        Initializes a MonthlyData model.

56        

57        Args:

58            month_number (str/int): The month number, such as 7 for July and 12 for December.

59            year (int):  The year

60            min_date (datetime obj): The minimum date in the query's date range

61            max_date (datetime obj): The maximum date in the query's date range

62            parameters (list): List of parameters to return data for

63            summary (boolean): True to return a summary of the daily data (maxs/mins/avgs), or False to return all data. Default is False/None.

64            file_type (string): The file extension (i.e. csv, json, html, txt) of the file output.

65            metric (bool): True to use metric units, False to use imperial units (default)

66        """

67        super(DownloadableFileView, self).__init__('xyz')

68        self.kwargs = kwargs

69        self.metric = metric

70        self.iso = iso

71        self.meteogram = meteogram

72        self.model = models.DownloadableData(self.iso, metric=self.metric, meteogram=self.meteogram, **kwargs)

73

74

75class AlertView(WebView):

76    

77    TEMPLATE = os.path.join(key.DIR_VIEWS, 'alert.html')

78    

79    def __init__(self, api_json):

80        """

81        Instantiates an alert view for the web viewer

82        

83        Args:

84            None

85        """

86        super(AlertView, self).__init__(api_json)

87    

88    @property

89    def html(self):

90        html = ''

91        template = util.load_template(self.TEMPLATE)

92        for alert_ in self.api_json.alerts:

93            alert = wx.Alert(alert_)

94            html += template.format(**vars())

95        return html

96

97

98class AllTimeRecordView(WebView):

99    

100    TEMPLATE = os.path.join(key.DIR_VIEWS, 'all-time-record.html')

101    

102    def __init__(self, metric=False):

103        super(AllTimeRecordView, self).__init__('xyz')

104        self.metric = metric

105        self.model = models.AllTimeRecord(self.metric)

106

107

108class AlmanacView(WebView):

109    

110    TEMPLATE = os.path.join(key.DIR_VIEWS, 'almanac.html')

111    

112    def __init__(self, api_json):

113        """

114        Instantiates an almanac view for the web viewer

115        

116        Args:

117            None

118        """

119        super(AlmanacView, self).__init__(api_json)

120        self.almanac = wx.Almanac(self.api_json)

121    

122    def to_imperial(self):

123        self.almanac.to_imperial()

124    

125    def to_metric(self):

126        self.almanac.to_metric()

127

128

129class ChartView(WebView):

130    

131    def __init__(self, iso=True, metric=DATABASE_SETTINGS.metric, **kwargs):

132        super(ChartView, self).__init__('zyx')

133        self.kwargs = kwargs

134        self.metric = metric

135        self.iso = iso

136    

137    @property

138    def html(self):

139        template_path = os.path.join(key.DIR_BODY_CONTENT, 'downloadable-plot.html')

140        template = util.load_template(template_path)

141        content = template.format(**vars())

142        return content

143    

144    @property

145    def json(self):

146        return JSONFileView(self.iso, self.metric, **self.kwargs)

147

148class CSVFileView(DownloadableFileView):

149    

150    def __init__(self, metric=DATABASE_SETTINGS.metric, **kwargs):

151        """

152        Initializes a MonthlyData model.

153        

154        Args:

155            month_number (str/int): The month number, such as 7 for July and 12 for December.

156            year (int):  The year

157            min_date (datetime obj): The minimum date in the query's date range

158            max_date (datetime obj): The maximum date in the query's date range

159            parameters (list): List of parameters to return data for

160            summary (boolean): True to return a summary of the daily data (maxs/mins/avgs), or False to return all data. Default is False/None.

161            file_type (string): The file extension (i.e. csv, json, html, txt) of the file output.

162            metric (bool): True to use metric units, False to use imperial units (default)

163        """

164        super(CSVFileView, self).__init__(metric=metric, **kwargs)

165    

166    @property

167    def content(self):

168        return self.generate_file_content()

169    

170    @property

171    def text_file_content(self):

172        return self.generate_file_content(tabbed_delimiter=True)

173    

174    def generate_file_content(self, tabbed_delimiter=False):

175        csv_file = StringIO()

176        if tabbed_delimiter:

177            wr = csv.writer(csv_file, delimiter="\t")

178        else:

179            wr = csv.writer(csv_file, quoting=csv.QUOTE_ALL)

180        wr.writerow(self.model.csv_header)

181        

182        for row in self.model.data:

183            wr.writerow(row)

184        

185        return csv_file.getvalue()

186

187

188class DataSummaryView(WebView):

189    

190    def __init__(self, metric=DATABASE_SETTINGS.metric, **kwargs):

191        super(DataSummaryView, self).__init__('xyz')

192        self.kwargs = kwargs

193        self.dates = list()

194        self.data = models.DataSummary(**kwargs)

195        for row in self.data.result:

196            self.dates.append(row[0])

197        self.metric = metric

198        self.temperature = units.Celsius(0)

199        self.wind = units.SpeedUnit(units.Kilometer(0), units.Hour(1))

200        self.precipitation = units.Millimeter(0)

201        self.pressure = units.Millibar(0)

202        self.visibility = units.Kilometer(0)

203        if not self.metric:

204            self.temperature = self.temperature.to_fahrenheit()

205            self.wind.to_mph()

206            self.precipitation = self.precipitation.to_inches()

207            self.pressure = self.pressure.to_inHg()

208            self.visibility = self.visibility.to_miles()

209    

210    @property

211    def date_header_string(self):

212        if len(self.dates) == 0:

213            try:

214                month = int(self.kwargs[key.MONTH_NUMBER])

215                year = int(self.kwargs[key.YEAR])

216                timestamp = datetime.date(year, month, 1)

217                return timestamp.strftime(key.T_MONTH_YEAR)

218            except:

219                return 'Cannot determine time period.'

220        

221        min_date = min(self.dates)

222        max_date = max(self.dates)

223        

224        if min_date.month == max_date.month and min_date.year == max_date.year:

225            header_string = min_date.strftime(key.T_MONTH_YEAR)

226        elif min_date.year == max_date.year:

227            min_date_string = min_date.strftime(key.T_FULL_MONTH_ONLY)

228            max_date_string = max_date.strftime(key.T_MONTH_YEAR)

229            header_string = '{} to {}'.format(min_date_string, max_date_string)

230        else:

231            min_date_string = min_date.strftime(key.T_MONTH_YEAR)

232            max_date_string = max_date.strftime(key.T_MONTH_YEAR)

233            header_string = '{} to {}'.format(min_date_string, max_date_string)

234        

235        return header_string

236    

237    @property

238    def last_time_it_rained(self):

239        now_utc = stg.UTC.localize(datetime.datetime.utcnow())

240        conn = DatabaseConnection()

241        query = 'SELECT `timestamp` FROM `outdoor` WHERE `daily_precipitation` > 0 ORDER BY `id` DESC LIMIT 1;'

242        result = conn.query_one(query)[0]

243        last_rain_obj_utc = stg.UTC.localize(result)

244        last_rain_obj = last_rain_obj_utc.astimezone(DATABASE_SETTINGS.local_timezone)

245        last_rain_string = last_rain_obj.strftime(key.T_FULL_DATE)

246        now = now_utc.astimezone(DATABASE_SETTINGS.local_timezone)

247        t_delta = now - last_rain_obj

248        delta_string = '{}     ({} days ago)'.format(last_rain_string, t_delta.days)

249        return delta_string

250    

251    @property

252    def last_rain_html(self):

253        template_path = os.path.join(key.DIR_INDIVIDUAL_ITEMS, 'last-time-it-rained.html')

254        template = util.load_template(template_path)

255        html = template.format(**vars())

256        return html

257    

258    @property

259    def month_html_selector(self):

260        html = key.EMPTY_STRING

261        template_path = os.path.join(key.DIR_INDIVIDUAL_ITEMS, 'select-option-item.html')

262        template = util.load_template(template_path)

263        year = 2000

264        day = 1

265        

266        for i in range(13):

267            if i == 0:

268                month_number = key.EMPTY_STRING

269                month_name = '--- Select a Month ---'

270            else:

271                date_object = datetime.date(year, i, day)

272                month_number = date_object.month

273                month_name = date_object.strftime(key.T_FULL_MONTH_ONLY)

274            

275            html += template.format(**vars())

276        

277        return html

278        

279    @property

280    def table_content(self):

281        template_path = os.path.join(key.DIR_INDIVIDUAL_ITEMS, 'data-summary-table-row.html')

282        template = util.load_template(template_path)

283        html = ''

284        if len(self.data.result) == 0:

285            return ''

286        for row in self.data.result:

287            date = row[0].strftime(key.T_MONTH_SLASH_DAY)

288            max_temp = units.Celsius(row[1])

289            min_temp = units.Celsius(row[2])

290            avg_temp = units.Celsius(row[3])

291            dewpoint = 'N/A'

292            feels_like_temp = 'N/A'

293            max_rh = units.Humidity(row[4], units.Celsius(10))

294            min_rh = units.Humidity(row[5], units.Celsius(10))

295            avg_rh = units.Humidity(row[6], units.Celsius(10))

296            precip = units.Millimeter(row[7], 2)

297            max_pressure = units.Millibar(row[8])

298            min_pressure = units.Millibar(row[9])

299            wind_direction = units.Degree(row[10])

300            wind_speed = units.SpeedUnit(units.Kilometer(row[12]), units.Hour(1))

301            max_gust = units.SpeedUnit(units.Kilometer(row[11]), units.Hour(1))

302            max_visibility = units.Kilometer(row[13], 1)

303            min_visibility = units.Kilometer(row[14], 1)

304            

305            if not self.metric:

306                max_temp = max_temp.to_fahrenheit()

307                min_temp = min_temp.to_fahrenheit()

308                avg_temp = avg_temp.to_fahrenheit()

309                

310                precip = precip.to_inches()

311                

312                max_pressure = max_pressure.to_inHg()

313                min_pressure = min_pressure.to_inHg()

314                

315                wind_speed.to_mph()

316                max_gust.to_mph()

317                

318                max_visibility = max_visibility.to_miles()

319                min_visibility = min_visibility.to_miles()

320            

321            html += template.format(**vars())

322        

323        return html

324    

325    @property

326    def period_summary_table(self):

327        html = self.generate_period_summary_table_html(self.data.period_summary)

328        return html

329    

330    @property

331    def yearly_summary_table(self):

332        html = self.generate_period_summary_table_html(self.data.yearly_summary, yearly=True)

333        return html

334    

335    def generate_period_summary_table_html(self, result, yearly=False):

336        template_path = os.path.join(key.DIR_TABLES, 'period-summary-table.html')

337        template = util.load_template(template_path)

338        if result == tuple([None] * len(result)):

339            return 'There is no data available for the time period you selected.'

340        max_temp = units.Celsius(result[0])

341        min_temp = units.Celsius(result[1])

342        max_rh = units.Humidity(result[2], max_temp)

343        min_rh = units.Humidity(result[3], min_temp)

344        max_sustained = units.SpeedUnit(units.Kilometer(result[4]), units.Hour(1))

345        max_gust = units.SpeedUnit(units.Kilometer(result[5]), units.Hour(1))

346        max_daily_precipitation = units.Millimeter(result[6], 2)

347        total_precipitation = self.data.total_precipitation if yearly else self.data.total_precipitation_monthly

348        

349        if not self.metric:

350            max_temp = max_temp.to_fahrenheit()

351            min_temp = min_temp.to_fahrenheit()

352            max_sustained.to_mph()

353            max_gust.to_mph()

354            total_precipitation = total_precipitation.to_inches()

355            max_daily_precipitation = max_daily_precipitation.to_inches()

356        

357        if yearly:

358            time_period = key.YEAR.capitalize()

359        elif self.data.month_number:

360            time_period = key.MONTH.capitalize()

361        else:

362            time_period = key.PERIOD.capitalize()

363        

364        html = template.format(**vars())

365        return html

366    

367    def set_max(self, obj, prop_string):

368        prop = getattr(self, prop_string)

369        if prop is None or obj.value > prop.value:

370            setattr(self, prop_string, obj)

371    

372    def set_min(self, obj, prop_string):

373        prop = getattr(self, prop_string)

374        if prop is None or obj.value < prop.value:

375            setattr(self, prop_string, obj)

376

377

378class FooterView(WebView):

379    

380    TEMPLATE = os.path.join(key.DIR_HTML, 'footer.html')

381    

382    def __init__(self, metric=stg.METRIC, environ=None):

383        """

384        Instantiates a footer view for the web viewer

385        

386        Args:

387            metric (bool): [Optional] Set to true to use metric units, false to use imperial units. If omitted, will look at settings to determine if the view should default to metric or imperial.

388            environ (list): [Optional] Include the environ variable in order to set query strings for the links.

389        """

390        super(FooterView, self).__init__(False)

391        self.metric = metric

392        self.year = datetime.datetime.now().year

393        self.query_string = environ[key.QUERY_STRING] if environ else None

394    

395    @property

396    def unit_link_href(self):

397        metric_units = 'units=metric'

398        

399        if self.query_string and self.query_string != metric_units:

400            metric_units = key.AMPERSAND + metric_units

401            query_string = '?' + self.query_string.replace(metric_units, key.EMPTY_STRING)

402            link = query_string if self.metric else query_string + metric_units

403        else:

404            link = key.DOT if self.metric else '?' + metric_units

405        

406        return link

407    

408    @property

409    def unit_link_text(self):

410        return key.HDR_IMPERIAL if self.metric else key.HDR_METRIC

411    

412    def to_imperial(self):

413        self.metric = False

414    

415    def to_metric(self):

416        self.metric = True

417

418

419class ForecastView(WebView):

420    

421    TEMPLATE = os.path.join(key.DIR_VIEWS, 'forecast.html')

422    

423    def __init__(self, api_json, title=True):

424        """

425        Instantiates an forecast view for the web viewer

426        

427        Args:

428            None

429        """

430        super(ForecastView, self).__init__(api_json)

431        self.forecast = wx.Forecast(self.api_json)

432        self.title = 'Forecast' if title else key.EMPTY_STRING

433    

434    @property

435    def html(self):

436        template = util.load_template(self.TEMPLATE)

437        period_html = key.EMPTY_STRING

438        

439        for period in self.forecast.forecast_periods:

440            period_view = ForecastPeriodView(period)

441            period_html += period_view.html

442        

443        html = template.format(**vars())

444        return html

445    

446    def to_imperial(self):

447        self.forecast.to_imperial()

448    

449    def to_metric(self):

450        self.forecast.to_metric()

451

452

453class ForecastPeriodView(WebView):

454    

455    TEMPLATE = os.path.join(key.DIR_VIEWS, 'forecast-period.html')

456    

457    def __init__(self, forecast_period_object):

458        """

459        Instantiates an forecast period view for the web viewer

460        

461        Args:

462            forecast_period_object (object): A weather.ForecastPeriod object. These are embedded in the Forecast object and can be called from a for loop.

463        """

464        super(ForecastPeriodView, self).__init__(False)

465        self.model = forecast_period_object

466    

467    @property

468    def html(self):

469        template = util.load_template(self.TEMPLATE)

470        html = template.format(**vars())

471        return html

472    

473    @property

474    def pops_string(self):

475        if self.model.pops > 0:

476            pops_string_vars = [key.HDR_POPS, self.model.pops]

477        else:

478            pops_string_vars = [key.HDR_AVG_RH, self.model.avg_rh]

479        pstring = key.SINGLE_LINE_LABEL_TEMPLATE.format(*pops_string_vars)

480        pops_string = '{}%'.format(pstring)

481        return pops_string

482    

483    def to_imperial(self):

484        self.model.to_imperial()

485    

486    def to_metric(self):

487        self.model.to_metric()

488

489

490class FormView(WebView):

491    

492    def __init__(self, template):

493        """

494        Instantiates a FormView.

495        

496        Args:

497            template (str): The filename of the template (do not include the path directories)

498        """

499        super(FormView, self).__init__('zyx')

500        self.TEMPLATE = template

501

502

503class HeadView(WebView):

504    

505    TEMPLATE = os.path.join(key.DIR_HTML, 'head.html')

506    

507    def __init__(self, *javascript_file_links):

508        """

509        Instantiates a view that represents the <head> tags in the HTML document

510        

511        Args:

512            *javascript_file_links (list): A list of URL's or href's to the JavaScript files

513        """

514        super(HeadView, self).__init__(False)

515        self.javascript_file_links = javascript_file_links

516        self.javascript_html = ''

517        for file_ in self.javascript_file_links:

518            self.javascript_html += util.generate_javascript_link(file_)

519

520

521class HeaderView(WebView):

522    

523    TEMPLATE = os.path.join(key.DIR_HTML, 'header.html')

524    

525    def __init__(self):

526        """

527        Instantiates a header view for the web viewer

528        

529        """

530        super(HeaderView, self).__init__(False)

531

532

533class HTMLFileView(DownloadableFileView):

534    

535    def __init__(self, metric=DATABASE_SETTINGS.metric, **kwargs):

536        """

537        Initializes a MonthlyData model.

538        

539        Args:

540            month_number (str/int): The month number, such as 7 for July and 12 for December.

541            year (int):  The year

542            min_date (datetime obj): The minimum date in the query's date range

543            max_date (datetime obj): The maximum date in the query's date range

544            parameters (list): List of parameters to return data for

545            summary (boolean): True to return a summary of the daily data (maxs/mins/avgs), or False to return all data. Default is False/None.

546            file_type (string): The file extension (i.e. csv, json, html, txt) of the file output.

547            metric (bool): True to use metric units, False to use imperial units (default)

548        """

549        super(HTMLFileView, self).__init__(metric=metric, **kwargs)

550        self.head_view = HeadView()

551    

552    @property

553    def content(self):

554        template_path = os.path.join(key.DIR_TABLES, 'data-dump-table.html')

555        row_template_path = os.path.join(key.DIR_INDIVIDUAL_ITEMS, 'data-dump-table-row.html')

556        template = util.load_template(template_path)

557        row_template = util.load_template(row_template_path)

558        table = StringIO()

559        row_content = ''

560        

561        for head in self.model.csv_header:

562            row_content += '\t\t<th>{}</th>\n'.format(head)

563        

564        header_row = row_template.format(**vars())

565        table_content = ''

566        

567        for row in self.model.data:

568            row_content = ''

569            

570            for item in row:

571                row_content += '\t\t<td>{}</td>\n'.format(item)

572            

573            table_content += row_template.format(**vars())

574        

575        table_html = template.format(**vars())

576        table.write(table_html)

577        return table.getvalue()

578

579

580class JSONFileView(DownloadableFileView):

581    

582    def __init__(self, iso=None, metric=DATABASE_SETTINGS.metric, meteogram=False, **kwargs):

583        """

584        Initializes a MonthlyData model.

585        

586        Args:

587            month_number (str/int): The month number, such as 7 for July and 12 for December.

588            year (int):  The year

589            min_date (datetime obj): The minimum date in the query's date range

590            max_date (datetime obj): The maximum date in the query's date range

591            parameters (list): List of parameters to return data for

592            summary (boolean): True to return a summary of the daily data (maxs/mins/avgs), or False to return all data. Default is False/None.

593            file_type (string): The file extension (i.e. csv, json, html, txt) of the file output.

594            metric (bool): True to use metric units, False to use imperial units (default)

595        """

596        self.iso = iso

597        super(JSONFileView, self).__init__(self.iso, metric, meteogram, **kwargs)

598    

599    @property

600    def content(self):

601        json_file_list = list()

602        

603        for row in self.model.data:

604            data_dict = dict()

605            

606            for k, v in zip(self.keys, row):

607                data_dict[k] = v

608            

609            json_file_list.append(data_dict)

610        

611        json_dict = {

612            'metricUnits': self.metric,

613            'list': json_file_list,

614            'tempUnits': self.model.temp_unit.ABBREVIATION,

615            'windUnits': self.model.wind_speed_unit.ABBREVIATION,

616            'precipitationUnits': self.model.precip_unit.ABBREVIATION,

617            'pressureUnits': self.model.pressure_unit.ABBREVIATION,

618            'visibilityUnits': self.model.visibility_unit.ABBREVIATION,

619        }

620        

621        return json.dumps(json_dict)

622    

623    @property

624    def keys(self):

625        if self.model.summary:

626            keys = [

627                'date',

628                'maxTemp',

629                'minTemp',

630                'avgTemp',

631                'maxRH',

632                'minRH',

633                'avgRH',

634                'precipitation',

635                'avgWindDirection',

636                'avgWindSpeed',

637                'maxGust',

638                'maxPressure',

639                'minPressure',

640                'maxVisibility',

641                'minVisibility',

642            ]

643        

644        else:

645            keys = [

646                'timestamp',

647                'conditions',

648                'temperature',

649                'dewpoint',

650                'windDirection',

651                'windSpeed',

652                'windGust',

653                'relativeHumidity',

654                'pressure',

655                'precipitation',

656                'visibility',

657                'alerts',

658            ]

659            

660            if self.meteogram:

661                keys_to_append = [

662                    'displayWindGust',

663                    'displayHeatIndex',

664                    'displayWindChill',

665                    'heatIndex',

666                    'windChill',

667                ]

668                

669                for k in keys_to_append:

670                    keys.append(k)

671        

672        return keys

673

674

675class MeteogramView(WebView):

676    

677    TEMPLATE = os.path.join(key.DIR_VIEWS, 'meteogram.html')

678    

679    def __init__(self):

680        super(MeteogramView, self).__init__('xyz')

681    

682    @property

683    def html(self):

684        template = util.load_template(self.TEMPLATE)

685        content = template.format(**vars())

686        return content

687

688

689class ObservationView(WebView):

690    

691    TEMPLATE = os.path.join(key.DIR_VIEWS, 'observations.html')

692    REGIONAL_WARNINGS_IMG = stg.REGIONAL_WARNINGS_IMG

693    

694    def __init__(self, api_json):

695        """

696        Loads data into the ObservationView

697        

698        Args:

699            api_json (object): A WundergroundApiJson() object.

700        """

701        super(ObservationView, self).__init__(api_json)

702        self.observation = wx.Observation(self.api_json, from_database=True)

703    

704    def to_imperial(self):

705        self.observation.to_imperial()

706    

707    def to_metric(self):

708        self.observation.to_metric()

709

710

711class PlotsView(WebView):

712    

713    TEMPLATE = os.path.join(key.DIR_VIEWS, 'plots.html')

714    PLOT_DIRECTORY = key.WEB_PLOT_DIRECTORY

715    

716    def __init__(self):

717        """

718        Instantiates a plots view for the web viewer

719        

720        Args:

721            None

722        """

723        super(PlotsView, self).__init__(False)

724

725

726class PDFDataDumpView(DownloadableFileView):

727    

728    def __init__(self, metric=DATABASE_SETTINGS.metric, **kwargs):

729        """

730        Initializes a MonthlyData model.

731        

732        Args:

733            month_number (str/int): The month number, such as 7 for July and 12 for December.

734            year (int):  The year

735            min_date (datetime obj): The minimum date in the query's date range

736            max_date (datetime obj): The maximum date in the query's date range

737            parameters (list): List of parameters to return data for

738            summary (boolean): True to return a summary of the daily data (maxs/mins/avgs), or False to return all data. Default is False/None.

739            file_type (string): The file extension (i.e. csv, json, html, txt) of the file output.

740            metric (bool): True to use metric units, False to use imperial units (default)

741        """

742        super(PDFDataDumpView, self).__init__(metric=metric, **kwargs)

743    

744    @property

745    def content(self):

746        col_labels = self.model.csv_header

747        cell_text = self.model.data

748        fig = plt.figure(figsize=(15, 15))

749        ax = fig.add_subplot(111)

750        ax.xaxis.set_visible(False)

751        ax.yaxis.set_visible(False)

752        the_table = ax.table(cellText=cell_text, colLabels=col_labels, loc='center')

753        pdf_file = BytesIO()

754        plt.tight_layout()

755        plt.savefig(pdf_file, format=key.EXTENSION_PDF)

756        return pdf_file.getvalue()

757

758

759class SQLFileView(DownloadableFileView):

760    

761    def __init__(self, metric=DATABASE_SETTINGS.metric, **kwargs):

762        """

763        Initializes a MonthlyData model.

764        

765        Args:

766            month_number (str/int): The month number, such as 7 for July and 12 for December.

767            year (int):  The year

768            min_date (datetime obj): The minimum date in the query's date range

769            max_date (datetime obj): The maximum date in the query's date range

770            parameters (list): List of parameters to return data for

771            summary (boolean): True to return a summary of the daily data (maxs/mins/avgs), or False to return all data. Default is False/None.

772            file_type (string): The file extension (i.e. csv, json, html, txt) of the file output.

773            metric (bool): True to use metric units, False to use imperial units (default)

774        """

775        super(SQLFileView, self).__init__(metric=metric, **kwargs)

776    

777    @property

778    def content(self):

779        template_path = os.path.join(key.DIR_DOWNLOADABLE_FILES, 'sql-data-dump.sql')

780        template = util.load_template(template_path)

781        

782        insert_columns = ', '.join(self.model.sql_header)

783        

784        table_info = ''

785        for name, description in zip(self.model.sql_header, self.model.sql_types):

786            table_info += '{} {}\n'.format(name, description)

787        

788        sql_data = ''

789        for row in self.model.data:

790            row = [str(x) for x in row]

791            data_string = ', '.join(row)

792            sql_data += '({})\n'.format(data_string)

793        

794        content = template.format(**vars())

795        return content

796

797

798class TravelForecastView(WebView):

799    

800    TEMPLATE = os.path.join(key.DIR_VIEWS, 'travel-forecast.html')

801    

802    def __init__(self):

803        """

804        Instantiates the Travel Forecast View

805        

806        Args:

807            None

808        """

809        super(TravelForecastView, self).__init__(False)

810        self.travel_forecast = wx.TravelForecast(from_database=True, **DATABASE_SETTINGS.travel_forecast)

811        self.forecast_view = ForecastView(self.travel_forecast.api_json, title=False)

812        self.alert_view = AlertView(self.travel_forecast.api_json)

813    

814    def to_imperial(self):

815        self.travel_forecast.to_imperial()

816        self.forecast_view.to_imperial()

817    

818    def to_metric(self):

819        self.travel_forecast.to_metric()

820        self.forecast_view.to_metric()