gas.py

Скрипт для рассчета среднего потребления газа (и не только) в месяц, если известны показания счетчика на определенные даты. для каждого интервала считает среднее потребление в сутки, потом суммирует по дням месяца - выводит потребление за месяц.

Сначала нужно ввести показания, и они будут сохранены в том-же каталоге, где и скрипт в файл "имя_скрипта_raw.json" - таким образом скрипт можно переименовать и считать отдельно потребление разных вещей - воды, газа, электричества...

Показания вводятся по одному за каждый запуск скрипта:

eking@eking-notebook:/home/eking$ ./gas.py3 2016-01-20 13456
eking@eking-notebook:/home/eking$ ./gas.py3 2016-02-13 13656
eking@eking-notebook:/home/eking$ ./gas.py3 2016-04-1 14000
eking@eking-notebook:/home/eking$ ./gas.py3 2016-09-1 15000

После чего нужно запустить скрипт без параметров:

eking@eking-notebook:/home/eking$ ./gas.py3

| Raw counters:          | Average by mounth:

| 2016-01-20       13456 | 2016-02                222
| 2016-02-13       13656 | 2016-03                222
| 2016-04-01       14000 | 2016-04                196
| 2016-09-01       15000 | 2016-05                203
                         | 2016-06                196
                         | 2016-07                203
                         | 2016-08                203
                         | 2016-09                  0

============================================================
| Total sum:        1544 | Total average sum:    1445

Разница в полной сумме и сумме усредненных - из-за округления. Собственно скрипт предназначен для оценки насколько изменяется среднее потребление. Чем чаще сделаны замеры - тем меньше будет ошибка округления.

gas.py3 (Источник)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'''
gas.py: для расчета потребления газа
'''

import sys
import json
import datetime


__author__ = 'Nikolay Gatilov'
__copyright__ = 'Nikolay Gatilov'
__license__ = 'GPL'
__version__ = '1.0.20161118'
__maintainer__ = 'Nikolay Gatilov'
__email__ = 'eking.work@gmail.com'

DEBUG = False

def load_data(df):
    try:
        with open(df, 'r') as f:
            data = json.load(f)
    except Exception as e:
        data = {}
        print ('Unable to read data from file:\n%s\n' % str(e))
    return data


def date_from_iso(s):
    (y, m, d) = map(int, s.split('-'))
    return datetime.date(y, m, d)


def convert_data(raw_d):
    raw_d_t = {}
    for i in raw_d.keys():
        raw_d_t[date_from_iso(i)] = int(raw_d[i])
    return raw_d_t


def get_first_date(raw_d):
    fd = min(raw_d)
    if fd.day > 1:
        return (fd.replace(day=1) + datetime.timedelta(days=32)).replace(day=1)
    else:
        return fd


def get_last_date(raw_d):
    fd = max(raw_d)
    nm = fd + datetime.timedelta(days=1)
    if fd.month != nm.month:
        return fd
    else:
        nm = fd.replace(day=1) + datetime.timedelta(days=32)
        return nm.replace(day=1) - datetime.timedelta(days=1)


def get_month_dates(k, month):
    md = []
    tmax = max(k)
    tdmax = tmax - month
    tmin = min(k)
    tdmin = month - tmin
    for i in k:
        if i.year == month.year and i.month == month.month:
            md.append(i)
        elif i > month and i - month < tdmax:
            tdmax = i - month
            tmax = i
        elif i < month and month - i < tdmin:
            tdmin = month - i
            tmin = i
    if tmax not in md:
        md.append(tmax)
    if tmin not in md and month not in md:
        md.append(tmin)
    return sorted(md)


def compute(md):
    k = md.keys()
    now = get_first_date(k)
    last_date = get_last_date(k)
    mad = {}
    while now < last_date:
        mda = get_month_dates(k, now)
        if DEBUG:
            print('\n', now, mda)
        nxt = (now + datetime.timedelta(days=32)).replace(day=1)
        mad[now] = 0
        h = mda.pop(0)
        for i in mda:
            r = (md[i]-md[h])
            if DEBUG:
                print('==> ', md[i], md[h], r)
            if h < now:
                mad[now] = mad[now] + r*abs(min(i, nxt) - now)/abs(i - h)
                if DEBUG:
                    print(' from h < now (',
                          min(i, nxt),
                          '-',
                          now,
                          ')/(',
                          i,
                          '-',
                          h,
                          ') = ',
                          mad[now])
            elif i > nxt:
                mad[now] = mad[now] + r*abs(nxt - max(h, now))/abs(i - h)
                if DEBUG:
                    print(' from i > nxt (',
                          nxt,
                          '-',
                          max(h, now),
                          ')/(',
                          i,
                          '-',
                          h,
                          ') = ',
                          mad[now])
            else:
                mad[now] = mad[now] + r
                if DEBUG:
                    print(' only add r ',
                          h,
                          i,
                          ' = ',
                          mad[now])
            h = i
        mad[now] = round(mad[now])
        if DEBUG:
            print('----- ', mad[now])
        now = nxt
    return mad


def save_data(df, data):
    try:
        with open(df, 'w') as f:
            json.dump(data, f, sort_keys=True)
    except Exception as e:
        print('Unable to save data in file:\n%s\n' % str(e))


if __name__ == "__main__":  # main
    RAW_DATA_FILE = '%s_raw.json' % sys.argv[0]
    AVG_DATA_FILE = '%s_avg.json' % sys.argv[0]
    raw_data = load_data(RAW_DATA_FILE)
    if len(sys.argv) > 2:
        di = sys.argv[1].split('.')
        count_in = int(sys.argv[2])
        da = date_from_iso(sys.argv[1])
        raw_data[da.isoformat()] = count_in
    else:
        by_month_data = convert_data(raw_data)
        avg_data = compute(by_month_data)
        avg_i = iter(sorted(avg_data.keys()))
        raw_i = iter(sorted(by_month_data.keys()))
        a_i_n = next(avg_i, None)
        r_i_n = next(raw_i, None)
        ma = by_month_data[max(by_month_data.keys())]
        mi = by_month_data[min(by_month_data.keys())]
        total_raw_sum = (ma-mi)
        total_avg_sum = 0
        print('\n| Raw counters:          | Average by mounth:\n')
        while a_i_n is not None or r_i_n is not None:
            if a_i_n is not None:
                ain_d = '{:%Y-%m}'.format(a_i_n)
                ain_n = '{:6d}'.format(avg_data[a_i_n])
                ain = '| %s             %s' % (ain_d, ain_n)
                total_avg_sum = total_avg_sum + avg_data[a_i_n]
            else:
                ain = ' ' * 14
            if r_i_n is not None:
                rin_d = '{:%Y-%m-%d}'.format(r_i_n)
                rin_n = '{:6d}'.format(by_month_data[r_i_n])
                rin = '| %s      %s' % (rin_d, rin_n)
            else:
                rin = ' ' * 24
            print(rin, ain)
            a_i_n = next(avg_i, None)
            r_i_n = next(raw_i, None)
        s = '\n{:s}\n| Total sum:     {:7d} | Total average sum: {:7d}\n'
        s = s.format('=' * 60, total_raw_sum, total_avg_sum)
        print(s)
        avg = {}
        for i in avg_data.keys():
            avg[i.isoformat()] = avg_data[i]
        save_data(AVG_DATA_FILE, avg)
    save_data(RAW_DATA_FILE, raw_data)