diff --git a/.github/workflows/api-deploy.yml b/.github/workflows/api-deploy.yml new file mode 100644 index 00000000..88037a92 --- /dev/null +++ b/.github/workflows/api-deploy.yml @@ -0,0 +1,60 @@ + name: API Deploy + + on: + push: + branches: + - main + paths: + - 'server/**/*' + + # https://github.com/codeigniter4/CodeIgniter4/blob/develop/.github/workflows/deploy-apidocs.yml + jobs: + api-ssh-deploy: + name: Production + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + # Docs: https://github.com/shivammathur/setup-php + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + extensions: mbstring, intl, curl, dom + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + working-directory: server + + - name: Cache composer dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('server/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-dev --no-progress --prefer-dist --optimize-autoloader + working-directory: server + + - name: Install LFTP + run: sudo apt install lftp + + - name: Configure LFTP + run: mkdir ~/.lftp && echo "set ssl:verify-certificate false;" >> ~/.lftp/rc + + - name: Load Secrets + run: echo "machine ${{ secrets.FTP_HOSTNAME }} login ${{ secrets.FTP_USERNAME }} password ${{ secrets.FTP_PASSWORD }}" > ~/.netrc + + - name: Upload Folder + run: | + lftp -e "put -O / .htaccess" ${{ secrets.FTP_HOSTNAME }} + lftp -e "rm -rf app" ${{ secrets.FTP_HOSTNAME }} + lftp -e "mirror --parallel=100 -R app" ${{ secrets.FTP_HOSTNAME }} + lftp -e "mirror --parallel=100 -R vendor" ${{ secrets.FTP_HOSTNAME }} + lftp -e "mirror --parallel=100 -R public" ${{ secrets.FTP_HOSTNAME }} + lftp -e "mirror --parallel=100 -R writable" ${{ secrets.FTP_HOSTNAME }} + working-directory: server diff --git a/.github/workflows/ui-checks.yml b/.github/workflows/ui-checks.yml index 08da3fa5..a3492526 100644 --- a/.github/workflows/ui-checks.yml +++ b/.github/workflows/ui-checks.yml @@ -5,64 +5,79 @@ on: branches: - main paths: - - 'frontend/**/*' + - 'client/**/*' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: ui-checks: + if: github.event.pull_request.draft == false + name: Build & Tests runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: - node-version: 18 - cache: 'npm' - cache-dependency-path: 'frontend/package-lock.json' + node-version: 20 + cache: 'yarn' + cache-dependency-path: 'client/yarn.lock' - name: Cache node modules - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache-npm with: path: | - frontend/node_modules + client/node_modules ~/.npm - key: ${{ runner.os }}-modules-${{ hashFiles('frontend/package-lock.json') }} + ${{ github.workspace }}/.next/cache + key: ${{ runner.os }}-modules-${{ hashFiles('client/yarn.lock') }} restore-keys: | ${{ runner.os }}-modules- ${{ runner.os }}- - if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }} name: Install dependencies - working-directory: frontend - run: npm ci -f + working-directory: client + run: yarn install - name: Linter - run: npm run eslint:check - working-directory: frontend + run: yarn eslint:check + working-directory: client - name: Prettier - run: npm run prettier:check - working-directory: frontend + run: yarn prettier:check + working-directory: client - name: Cache UI build - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache-build with: - path: client/out - key: ${{ runner.os }}-build-${{ hashFiles('frontend/**/*.ts', 'frontend/**/*.tsx') }} + path: client/.next + key: ${{ runner.os }}-build-${{ hashFiles('client/**/*.ts', 'client/**/*.tsx') }} restore-keys: | ${{ runner.os }}-build- ${{ runner.os }}- - if: ${{ steps.cache-build.outputs.cache-hit != 'true' }} - name: Build UI + name: Configure Client run: | - export dateNow=$(date +"%d.%m.%Y %H:%M") - echo "export const update = '$dateNow'" > src/update.ts - npm run build --if-present - working-directory: frontend + echo "NEXT_PUBLIC_API_HOST = '${{ secrets.NEXT_PUBLIC_API_HOST }}' + NEXT_PUBLIC_SITE_LINK = '${{ secrets.NEXT_PUBLIC_SITE_LINK }}'" > .env + working-directory: client + + # - name: UI Unit Tests + # run: yarn test + # working-directory: client - - name: Test - run: npm run test:coverage - working-directory: frontend + - if: ${{ steps.cache-build.outputs.cache-hit != 'true' }} + name: Build UI + run: | + export dateNow=$(date +"%Y-%m-%dT%H:%M") + echo "export const update = '$dateNow'" > update.ts + yarn build + working-directory: client diff --git a/.github/workflows/ui-deploy.yml b/.github/workflows/ui-deploy.yml index 6a2e0272..4d1fc790 100644 --- a/.github/workflows/ui-deploy.yml +++ b/.github/workflows/ui-deploy.yml @@ -1,72 +1,124 @@ -name: FTP Deploy +name: UI Deploy on: push: branches: - main paths: - - 'frontend/**/*' + - 'client/**/*' + + pull_request: + branches: + - main jobs: - ftp-ui-deploy: + ui-ssh-deploy: + name: Production runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: - node-version: 18 - cache: 'npm' - cache-dependency-path: 'frontend/package-lock.json' + node-version: 20 + cache: 'yarn' + cache-dependency-path: 'client/yarn.lock' - name: Cache node modules - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache-npm with: path: | - frontend/node_modules + client/node_modules ~/.npm - key: ${{ runner.os }}-modules-${{ hashFiles('frontend/package-lock.json') }} + ${{ github.workspace }}/.next/cache + key: ${{ runner.os }}-modules-${{ hashFiles('client/yarn.lock') }} restore-keys: | ${{ runner.os }}-modules- ${{ runner.os }}- - if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }} name: Install dependencies - working-directory: frontend - run: npm ci -f - - - name: Configure Client - run: echo "REACT_APP_API_HOST = '${{ secrets.CLIENT_API_HOST }}'" > .env - working-directory: frontend + working-directory: client + run: yarn install - name: Cache UI build - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache-build with: - path: client/out - key: ${{ runner.os }}-build-${{ hashFiles('frontend/**/*.ts', 'frontend/**/*.tsx') }} + path: client/.next + key: ${{ runner.os }}-build-${{ hashFiles('client/**/*.ts', 'client/**/*.tsx') }} restore-keys: | ${{ runner.os }}-build- ${{ runner.os }}- + - if: ${{ steps.cache-build.outputs.cache-hit != 'true' }} + name: Configure Client + run: | + echo "NEXT_PUBLIC_API_HOST = '${{ secrets.NEXT_PUBLIC_API_HOST }}' + NEXT_PUBLIC_SITE_LINK = '${{ secrets.NEXT_PUBLIC_SITE_LINK }}'" > .env + working-directory: client + - if: ${{ steps.cache-build.outputs.cache-hit != 'true' }} name: Build UI run: | - export dateNow=$(date +"%d.%m.%Y %H:%M") - echo "export const update = '$dateNow'" > src/update.ts - npm run build --if-present - working-directory: frontend + export dateNow=$(date +"%Y-%m-%dT%H:%M") + echo "export const update = '$dateNow'" > update.ts + yarn build + working-directory: client - - name: Install LFTP client - run: sudo apt install lftp + - name: Configure SSH + env: + SSH_HOST: ${{secrets.SSH_HOST}} + SSH_PORT: ${{secrets.SSH_PORT}} + SSH_USER: ${{secrets.SSH_USER}} + SSH_KEY: ${{secrets.SSH_KEY}} + run: | + mkdir -p ~/.ssh/ + echo "$SSH_KEY" > ~/.ssh/deploy + chmod 400 ~/.ssh/deploy + cat >>~/.ssh/config <> ~/.lftp/rc + # If we just use nginx and the application is exported to plain HTML + #- name: Send files + # run: rsync -avz -e ssh ./client/out/ vps:/var/www/meteo.miksoft.pro + + # If we run NextJS on a Node server using PM2 + - name: Send files + run: | + ssh vps "cd /var/www/meteo.miksoft.pro && rm -rf .next" + rsync -avz -e ssh client/.next/standalone/ vps:/var/www/meteo.miksoft.pro + rsync -avz -e ssh client/.next/static vps:/var/www/meteo.miksoft.pro/.next + rsync -avz -e ssh client/public vps:/var/www/meteo.miksoft.pro + rsync -avz -e ssh client/ecosystem.config.js vps:/var/www/meteo.miksoft.pro - - name: Load Secrets - run: echo "machine ${{ secrets.FTP_HOSTNAME }} login ${{ secrets.FTP_USERNAME }} password ${{ secrets.FTP_PASSWORD }}" > ~/.netrc + # For the first launch of the application on the server: + # pm2 start ecosystem.config.js && pm2 save + - name: Restart PM2 + run: ssh vps "pm2 restart meteo.miksoft.pro" - - name: Deploy UI - run: lftp -e "mirror --parallel=100 -R frontend/build/ /" ${{ secrets.FTP_HOSTNAME }} + # If for deployment we use not SSH, but an FTP connection + # - name: Export UI + # working-directory: client + # run: yarn export + # + # - name: Install LFTP + # run: sudo apt install lftp + # + # - name: Configure LFTP + # run: mkdir ~/.lftp && echo "set ssl:verify-certificate false;" >> ~/.lftp/rc + # + # - name: Load Secrets + # run: echo "machine ${{ secrets.FTP_HOSTNAME }} login ${{ secrets.FTP_USERNAME }} password ${{ secrets.FTP_PASSWORD }}" > ~/.netrc + # + # - name: Upload Folder + # run: lftp -e "mirror --parallel=100 -R client/out/ /" ${{ secrets.FTP_HOSTNAME }} diff --git a/backend/MySQL_dump_15.02.2022.sql b/backend/MySQL_dump_15.02.2022.sql deleted file mode 100644 index e3e3213d..00000000 --- a/backend/MySQL_dump_15.02.2022.sql +++ /dev/null @@ -1,145 +0,0 @@ --- phpMyAdmin SQL Dump --- version 4.9.7 --- https://www.phpmyadmin.net/ --- --- Хост: 10.0.0.231:3319 --- Время создания: Фев 15 2022 г., 03:39 --- Версия сервера: 10.3.30-MariaDB-log --- Версия PHP: 7.4.11 - -SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; -SET AUTOCOMMIT = 0; -START TRANSACTION; -SET time_zone = "+00:00"; - - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8mb4 */; - --- --- База данных: `mik_weather` --- - --- -------------------------------------------------------- - --- --- Структура таблицы `weather_current` --- - -CREATE TABLE `weather_current` ( - `item_id` varchar(13) NOT NULL COMMENT 'Unique ID', - `item_utc_date` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'DateTime in UTC', - `conditions` smallint(3) DEFAULT NULL, - `temperature` float DEFAULT NULL, - `feels_like` float DEFAULT NULL, - `humidity` float DEFAULT NULL, - `pressure` float DEFAULT NULL, - `clouds` smallint(3) DEFAULT NULL, - `wind_speed` float DEFAULT NULL, - `wind_deg` float DEFAULT NULL, - `wind_gust` float DEFAULT NULL, - `precipitation` float DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Current weather from station and OpenWeather'; - --- -------------------------------------------------------- - --- --- Структура таблицы `weather_forecast` --- - -CREATE TABLE `weather_forecast` ( - `item_id` varchar(20) NOT NULL COMMENT 'Unique ID', - `item_utc_date` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'DateTime in UTC', - `conditions` smallint(3) NOT NULL, - `temperature` float NOT NULL, - `feels_like` float NOT NULL, - `humidity` float NOT NULL, - `pressure` float NOT NULL, - `clouds` smallint(3) NOT NULL, - `wind_speed` float NOT NULL, - `wind_deg` float NOT NULL, - `wind_gust` float NOT NULL, - `precipitation` float NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Структура таблицы `weather_hourly` --- - -CREATE TABLE `weather_hourly` ( - `item_id` varchar(13) NOT NULL COMMENT 'Unique ID', - `item_utc_date` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'DateTime in UTC', - `temperature` float DEFAULT NULL, - `humidity` float DEFAULT NULL, - `pressure` float DEFAULT NULL, - `illumination` smallint(5) DEFAULT NULL, - `uvindex` float DEFAULT NULL, - `wind_speed` float DEFAULT NULL, - `wind_deg` smallint(3) DEFAULT NULL, - `wind_gust` float DEFAULT NULL, - `clouds` smallint(3) DEFAULT NULL, - `precipitation` float DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Структура таблицы `weather_sensors` --- - -CREATE TABLE `weather_sensors` ( - `item_id` varchar(13) NOT NULL COMMENT 'Unique ID', - `item_utc_date` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'DateTime in UTC', - `temperature` float DEFAULT NULL, - `humidity` float DEFAULT NULL, - `pressure` float DEFAULT NULL, - `illumination` smallint(5) DEFAULT NULL, - `uvindex` float DEFAULT NULL, - `wind_speed` float DEFAULT NULL, - `wind_deg` float DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Current weather from station and OpenWeather'; - --- --- Индексы сохранённых таблиц --- - --- --- Индексы таблицы `weather_current` --- -ALTER TABLE `weather_current` - ADD PRIMARY KEY (`item_id`), - ADD UNIQUE KEY `item_id` (`item_id`), - ADD KEY `item_utc_date` (`item_utc_date`); - --- --- Индексы таблицы `weather_forecast` --- -ALTER TABLE `weather_forecast` - ADD PRIMARY KEY (`item_id`), - ADD UNIQUE KEY `item_id` (`item_id`), - ADD KEY `item_utc_data` (`item_utc_date`); - --- --- Индексы таблицы `weather_hourly` --- -ALTER TABLE `weather_hourly` - ADD PRIMARY KEY (`item_id`), - ADD UNIQUE KEY `item_id` (`item_id`), - ADD KEY `item_utc_date` (`item_utc_date`); - --- --- Индексы таблицы `weather_sensors` --- -ALTER TABLE `weather_sensors` - ADD PRIMARY KEY (`item_id`), - ADD UNIQUE KEY `item_id` (`item_id`), - ADD KEY `item_utc_date` (`item_utc_date`); -COMMIT; - -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/backend/app/Api/Export.php b/backend/app/Api/Export.php deleted file mode 100644 index 22765ca5..00000000 --- a/backend/app/Api/Export.php +++ /dev/null @@ -1,103 +0,0 @@ -period = (object) [ - 'start' => date('Y-m-d H:i:s', strtotime($period->start)), - 'end' => date('Y-m-d H:i:s', strtotime($period->end . ' +1 day')) - ]; - - $this->period_days = calc_days_in_period($this->period->start, $this->period->end); - $this->average_time = get_means_minutes($this->period_days); - - $this->_fetch(); - - return $this->_make_data(); - } - - /** - * Returns the generated data array, the first element is the names of the sensors - * @return \string[][] - */ - protected function _make_data(): array - { - $result = [['UTC Datetime']]; - - foreach ($this->data as $key => $item) { - $_tmp_result = [$item->item_utc_date]; - - unset($item->item_utc_date, $item->item_id); - - foreach ($item as $sensor => $value) { - if ($key === 0) { - $result[$key][] = $sensor; - } - - $_tmp_result[] = (float) $value; - } - - $result[] = $_tmp_result; - } - - return $result; - } - - /** - * Selects from the database - * @return array|mixed|void - */ - protected function _fetch() - { - $sensors_available = ['temperature', 'humidity', 'pressure', 'illumination', 'uvindex', 'wind_speed', 'wind_deg']; - $current_available = ['temperature', 'humidity', 'pressure', 'wind_speed', 'wind_deg', 'wind_gust', 'clouds', 'precipitation']; - - // Если время обобщения данных 60 минут и более, то будем брать \ заполнять значения из сводной таблицы - if ($this->average_time >= 60) { - $keys = array_unique(array_merge($sensors_available, $current_available)); - - $Hourly = new Hourly(); - $Hourly->set_key_items($keys); - - return $this->data = $Hourly->get_period($this->period->start, $this->period->end); - } - - $Sensors = new Sensors(); - $Current = new Current(); - - $Sensors->set_key_items($sensors_available); - $Current->set_key_items($current_available); - - $data_sensors = $Sensors->get_period($this->period->start, $this->period->end); - $data_current = $Current->get_period($this->period->start, $this->period->end); - - $this->data = array_merge($data_sensors, $data_current); - } -} \ No newline at end of file diff --git a/backend/app/Api/Heatmap.php b/backend/app/Api/Heatmap.php deleted file mode 100644 index 9910e3fc..00000000 --- a/backend/app/Api/Heatmap.php +++ /dev/null @@ -1,60 +0,0 @@ -period = (object) [ - 'start' => date('Y-m-d 00:00:00', strtotime($period->start)), - 'end' => date('Y-m-d 23:59:59', strtotime($period->end)) - ]; - - // Для построения тепловой карты всегда только один датчик используется, берем первый, остальные - удаляем - $this->sensors = [array_shift($sensors)]; - $this->_fetch(); - - return $this->_make_chart_data(); - } - - protected function _make_chart_data() - { - if (empty($this->data)) { - return false; - } - - $result = []; - $sensor = array_shift($this->sensors); - $update = strtotime($this->data[0]->item_utc_date . ' UTC'); - - foreach ($this->data as $item) { - $timestamp = strtotime($item->item_utc_date . ' +5 hours'); - $result[] = [ - date('U', $timestamp) * 1000, - (int) date('H', $timestamp), - (float) $item->$sensor - ]; - } - - return (object) ['update' => $update, 'payload' => $result]; - } - - protected function _fetch() - { - $this->Hourly = new Hourly(); - - $this->Hourly->set_key_items($this->sensors); - $this->data = $this->Hourly->get_period($this->period->start, $this->period->end); - } -} \ No newline at end of file diff --git a/backend/app/Api/Statistic.php b/backend/app/Api/Statistic.php deleted file mode 100644 index 6e302351..00000000 --- a/backend/app/Api/Statistic.php +++ /dev/null @@ -1,217 +0,0 @@ -_init($period, $sensors); - return $this->_make_chart_data(); - } - - function get_sensors_data(object $period, array $sensors): object - { - $this->_init($period, $sensors); - - $data = $this->isMean() ? $this->dataMean : array_merge($this->dataBasic, $this->dataSpare); - - foreach ($data as $item) { - $item->date = strtotime($item->item_utc_date); - unset($item->item_utc_date); - } - - return (object) ['update' => $data[0]->date, 'payload' => $data]; - } - - protected function _init(object $period, array $sensors) - { - $this->period = (object) [ - 'start' => date(DATE_FORMAT, strtotime($period->start)), - 'end' => date(DATE_FORMAT, strtotime($period->end . ' +1 day')) // Включительно дату, а не ДО этой даты - ]; - - $this->sensors = $sensors; - - $this->period_days = calc_days_in_period($this->period->start, $this->period->end); - $this->average_time = get_means_minutes($this->period_days); - - $this->_fetch(); - } - - protected function _make_chart_data(): object - { - $begin = new \DateTime($this->period->start); - $end = new \DateTime($this->period->end); - - $interval = \DateInterval::createFromDateString($this->average_time . ' minutes'); - $result = []; - $chart = (object) []; - - if ($this->isMean()) { - $update = strtotime($this->dataMean[0]->item_utc_date . ' UTC'); - } else { - $basicDate = $this->dataBasic ? strtotime($this->dataBasic[0]->item_utc_date . ' UTC') : 0; - $spareDate = $this->dataSpare ? strtotime($this->dataSpare[0]->item_utc_date . ' UTC') : 0; - $update = max($basicDate, $spareDate); - } - - foreach (new \DatePeriod($begin, $interval, $end) as $dt) { - $tmp_date = $dt->format(DATE_FORMAT); - $next_date = date(DATE_FORMAT, $dt->format('U') + $this->average_time * 60); - - if ($this->isMean()) { - $tmp_array = $this->_make_time_array('dataMean', $tmp_date, $next_date); - } else { - $basic = $this->_make_time_array('dataBasic', $tmp_date, $next_date); - $spare = $this->_make_time_array('dataSpare', $tmp_date, $next_date); - - if (!empty($basic) || !empty($spare)) { - foreach ($this->sensors as $sensor) { - $tmp_array[$sensor] = isset($basic[$sensor]) && !empty($basic[$sensor]) - ? $basic[$sensor] - : ($spare[$sensor] ?? null); - - if ($tmp_array[$sensor] === null) { - unset($tmp_array[$sensor]); - } - } - } else { - $tmp_array = null; - } - } - - if (empty($tmp_array)) { - continue; - } - - $result[$tmp_date] = $tmp_array; - - foreach ($result[$tmp_date] as $item => $value) { - if ($item === 'counter') { - continue; - } - - // Заполняем значения для графиков - $chart->$item[] = [ - date('U', strtotime($tmp_date . ' UTC')) * 1000, - $value - ]; - } - - // Удаляем текущий элемент массива - unset($result[$tmp_date]); - } - - return (object) ['update' => $update, 'payload' => $chart]; - } - - // Создаем массив по временным промежутком с суммой показаний всех запрошенных датчиков - protected function _make_time_array($source, $tmp_date, $next_date): array - { - $return = (object) []; - $result = (object) []; - $counts = (object) []; - - foreach ($this->$source as $sensor_key => $item) { - $_item_date = date(DATE_FORMAT, strtotime($item->item_utc_date . ' +5 hours')); - - // Перебираем весь массив значений датчиков, если текущие показания не в промежутке дат, то пропускаем - if ($_item_date < $tmp_date || $_item_date > $next_date) { - continue; - } - - // Удаляем служебные поля - unset($item->item_utc_date); - - // Перебираем объект текущих датчиков - foreach ($item as $key => $value) { - // Если такого датчика нет в массиве текущей даты - то создаем его, иначе плюсуем - if (!isset($result->$key)) { - $result->$key = $value; - $counts->$key = 1; - } else { - $result->$key += $value; - $counts->$key += 1; - } - } - - // Удаляем текущие показания из массива датчиков, т.к. мы их уже занесли - unset($this->$source[$sensor_key]); - } - - foreach ($result as $item => $value) { - $return->$item = round($value / $counts->$item, 1); - } - - return (array) $return; - } - - protected function _fetch() - { - $sensors_available = ['temperature', 'humidity', 'pressure', 'illumination', 'uvindex', 'wind_speed', 'wind_deg']; - $current_available = ['temperature', 'humidity', 'pressure', 'wind_speed', 'wind_deg', 'wind_gust', 'clouds', 'precipitation']; - - // Если время обобщения данных 60 минут и более, то будем брать \ заполнять значения из сводной таблицы - if ($this->isMean()) { - $keys = $this->_get_available_keys(array_unique(array_merge($sensors_available, $current_available))); - - $this->Hourly = new Hourly(); - $this->Hourly->set_key_items($keys); - $this->dataMean = $this->Hourly->get_period($this->period->start, $this->period->end); - } - - $this->Sensors = new Sensors(); - $this->Current = new Current(); - - $this->Sensors->set_key_items($this->_get_available_keys($sensors_available)); - $this->Current->set_key_items($this->_get_available_keys($current_available)); - - $this->dataBasic = $this->Sensors->get_period($this->period->start, $this->period->end); - $this->dataSpare = $this->Current->get_period($this->period->start, $this->period->end); - } - - protected function isMean(): bool - { - return $this->average_time >= 60; - } - - // Получаем список доступных ключей сенсоров - protected function _get_available_keys($list): array - { - $result = []; - - foreach ($this->sensors as $item) { - if (in_array($item, $list)) { - $result[] = $item; - } - } - - return $result; - } -} \ No newline at end of file diff --git a/backend/app/Api/Uptime.php b/backend/app/Api/Uptime.php deleted file mode 100644 index c2397d49..00000000 --- a/backend/app/Api/Uptime.php +++ /dev/null @@ -1,25 +0,0 @@ -Sensors = new Sensors(); - } - - function get_uptime(): object - { - $should = 1440; - $count = (int) $this->Sensors->get_week_count()->item_id; - $last = $this->Sensors->get_last_row()->item_utc_date; - $uptime = round(($count / $should) * 100, 1); - - return (object) [ - 'update' => strtotime($last . ' UTC'), - 'payload' => min($uptime, 99.9) - ]; - } -} \ No newline at end of file diff --git a/backend/app/Api/Weather.php b/backend/app/Api/Weather.php deleted file mode 100644 index b261bc2d..00000000 --- a/backend/app/Api/Weather.php +++ /dev/null @@ -1,118 +0,0 @@ -Sensors = new Sensors(); - $this->Current = new Current(); - $this->Forecast = new Forecast(); - } - - function get_sensors(): object - { - $result = []; - $weather = $this->Current->get_array_by_last_day(); - $sensors = $this->Sensors->get_array_by_last_day(); - - $time_diff = (! empty($sensors)) ? - round(abs(strtotime($sensors[0]->item_utc_date) - strtotime($weather[0]->item_utc_date)) / 60,0) : - round(strtotime($weather[0]->item_utc_date) / 60,0); - - $data_actual = $time_diff < self::TIME_ACTUAL; - $time_update = strtotime(($data_actual ? $sensors[0]->item_utc_date : $weather[0]->item_utc_date) . ' UTC') ; - - $result[] = new SensorItem(($data_actual ? $sensors : $weather), 'temperature'); - $result[] = new SensorItem($weather, 'feels_like'); - $result[] = new SensorItem($this->_create_dewpoint($data_actual ? $sensors : $weather), 'dewpoint'); - $result[] = new SensorItem(($data_actual ? $sensors : $weather), 'humidity'); - $result[] = new SensorItem(($data_actual ? $sensors : $weather), 'pressure'); - $result[] = new SensorItem(($data_actual ? $sensors : $weather), 'wind_speed'); - $result[] = new SensorItem($weather, 'wind_gust'); - $result[] = new SensorItem(($data_actual ? $sensors : $weather), 'wind_deg'); - $result[] = new SensorItem($weather, 'clouds'); - $result[] = new SensorItem($weather, 'precipitation'); - - if ($data_actual) { - $result[] = new SensorItem($sensors, 'illumination'); - $result[] = new SensorItem($sensors, 'uvindex'); - } - - return (object) ['update' => $time_update, 'payload' => $result]; - } - - function get_forecast(): object - { - $weather = []; - $forecast = $this->Forecast->get_last(); - - foreach ($forecast as $item) { - $weather[] = (object) [ - 'time' => strtotime($item->item_utc_date . ' UTC'), - 'condition_id' => (int) $item->conditions, - 'temperature' => (float) $item->temperature, - 'clouds' => (int) $item->clouds, - 'precipitation' => (float) $item->precipitation, - ]; - } - - return (object) ['update' => (cache('forecast') ? cache('forecast') : gmdate('U')), 'payload' => $weather]; - } - - function get_last(): object - { - $sensors = $this->Sensors->get_last_row(); - $current = $this->Current->get_last_row(); - - $time_diff = round(abs(strtotime($sensors->item_utc_date) - strtotime($current->item_utc_date)) / 60,0); - - $actual = $time_diff < self::TIME_ACTUAL; - $outdated = $time_diff > self::TIME_OUTDATED; - - $update = strtotime(($actual ? $sensors->item_utc_date : $current->item_utc_date) . ' UTC'); - $weather = [ - 'condition_id' => (int) $current->conditions, - 'temperature' => (float) ($actual ? $sensors->temperature : $current->temperature), - 'temperature_feels' => (float) $current->feels_like, - 'humidity' => (float) ($actual ? $sensors->humidity : $current->humidity), - 'pressure' => (float) ($actual ? $sensors->pressure : $current->pressure), - 'wind_speed' => (float) ($actual ? $sensors->wind_speed : $current->wind_speed), - 'wind_gust' => (float) $current->wind_gust, - 'wind_degree' => (int) ($actual ? $sensors->wind_deg : $current->wind_deg), - 'clouds' => (int) $current->clouds, - 'precipitation' => (float) $current->precipitation, - 'illumination' => ! $outdated ? (int) $sensors->illumination : null, - 'uvindex' => ! $outdated ? (float) $sensors->uvindex : null, - ]; - - return (object) ['update' => $update, 'payload' => $weather]; - } - - function _create_dewpoint($data): array - { - $temp = []; - - foreach ($data as $item) { - $temp[] = (object) [ - 'item_utc_date' => $item->item_utc_date, - 'dewpoint' => round(((pow(($item->humidity / 100), 0.125)) * (112 + 0.9 * $item->temperature) + (0.1 * $item->temperature) - 112), 1) - ]; - } - - return $temp; - } -} \ No newline at end of file diff --git a/backend/app/Api/WindRose.php b/backend/app/Api/WindRose.php deleted file mode 100644 index ae49ebf2..00000000 --- a/backend/app/Api/WindRose.php +++ /dev/null @@ -1,230 +0,0 @@ -_init($period); - return $this->_make_chart_data(); - } - - protected function _init(object $period) - { - $this->period = (object) [ - 'start' => date(DATE_FORMAT, strtotime($period->start)), - 'end' => date(DATE_FORMAT, strtotime($period->end . ' +1 day')) // Включительно дату, а не ДО этой даты - ]; - - $this->sensors = ['wind_speed', 'wind_deg']; - - $this->period_days = calc_days_in_period($this->period->start, $this->period->end); - $this->average_time = get_means_minutes($this->period_days); - - $this->_fetch(); - } - - protected function _make_chart_data(): object - { - $_temp_wd = [0, 0, 0, 0, 0, 0, 0, 0]; // Array of wind directions (8 bit) - $_temp_wr = $this->create_wind_rose_array(); // Wind rose array - $_temp_wr_total = 0; // Wind rose count items - - if ($this->isMean()) { - $update = strtotime($this->dataMean[0]->item_utc_date . ' UTC'); - $tmp_array = $this->dataMean; - } else { - $basicDate = $this->dataBasic ? strtotime($this->dataBasic[0]->item_utc_date . ' UTC') : 0; - $spareDate = $this->dataSpare ? strtotime($this->dataSpare[0]->item_utc_date . ' UTC') : 0; - $update = max($basicDate, $spareDate); - $tmp_array = !empty($this->dataBasic) ? $this->dataBasic : $this->dataSpare; - } - - foreach ($tmp_array as $item) { - if ((int) $item->wind_speed === 0) { - continue; - } - - $_tmp_wind_position = $this->convert_degree_to_direct($item->wind_deg); - - $_temp_wd[$_tmp_wind_position]++; - $_temp_wr[$this->convert_wind_speed($item->wind_speed)][$_tmp_wind_position]++; - $_temp_wr_total++; - } - - $result = $this->_insert_wind_direction($_temp_wd, $_temp_wr, $_temp_wr_total); - - return (object) ['update' => $update, 'payload' => $result]; - } - - protected function _fetch() - { - // Если время обобщения данных 60 минут и более, то будем брать \ заполнять значения из сводной таблицы - if ($this->isMean()) { - $this->Hourly = new Hourly(); - $this->Hourly->set_key_items($this->sensors); - return $this->dataMean = $this->Hourly->get_period($this->period->start, $this->period->end); - } - - $this->Sensors = new Sensors(); - $this->Current = new Current(); - - $this->Sensors->set_key_items($this->sensors); - $this->Current->set_key_items($this->sensors); - - $this->dataBasic = $this->Sensors->get_period($this->period->start, $this->period->end); - $this->dataSpare = $this->Current->get_period($this->period->start, $this->period->end); - } - - protected function isMean(): bool - { - return $this->average_time >= 60; - } - - - function create_wind_rose_array(): array - { - $_array = []; - - // Wind speed - for ($i = 0; $i <= 6; $i++) { - // Wind direction - for ($k = 0; $k <= 7; $k++) { - $_array[$i][$k] = 0; - } - } - - return $_array; - } - - function convert_degree_to_direct($degree): int - { - if ($degree == 0) return 0; - - $_range = [ - '0' => ['min' => 337, 'max' => 22], - '1' => ['min' => 22, 'max' => 67], - '2' => ['min' => 67, 'max' => 112], - '3' => ['min' => 112, 'max' => 157], - '4' => ['min' => 157, 'max' => 202], - '5' => ['min' => 202, 'max' => 247], - '6' => ['min' => 247, 'max' => 292], - '7' => ['min' => 292, 'max' => 337], - ]; - - foreach ($_range as $index => $group) { - if ($degree >= $group['min'] && $degree < $group['max']) { - return $index; - } - } - - return 0; - } - - /** - * Convert wind speed to index for wind rose chart - * @param $wind_speed - * @return int - */ - function convert_wind_speed($wind_speed): int - { - $wind_speed = (int) $wind_speed; - - if ($wind_speed > 0 && $wind_speed <= 1) - return 0; - else if ($wind_speed > 1 && $wind_speed <= 3) - return 1; - else if ($wind_speed > 3 && $wind_speed <= 5) - return 2; - else if ($wind_speed > 5 && $wind_speed <= 7) - return 3; - else if ($wind_speed > 7 && $wind_speed <= 9) - return 4; - else - return 5; - } - - /** - * Determine the frequency from which direction the wind blows - * @param array $_temp_wd - * @param $_temp_wr - * @param $_temp_wr_total - * @return array - */ - protected function _insert_wind_direction(array $_temp_wd, $_temp_wr, $_temp_wr_total): array - { - $_tmp = $_temp_wd; - - sort($_tmp); - - return $this->calculate_wind_rose($_temp_wr, $_temp_wr_total); - } - - /** - * Calculate wind rose - * @param $_temp_wr - * @param $_temp_wr_total - * @return array|null - */ - function calculate_wind_rose($_temp_wr, $_temp_wr_total): ?array - { - $new_wr_array = []; - - foreach ($_temp_wr as $key_wr_1 => $val_wr_direct) { - foreach ($val_wr_direct as $key_wr_2 => $val_wr_speed) { - $_tmp = $_temp_wr_total > 0 ? round((($val_wr_speed / $_temp_wr_total) * 100), 1) : 0; - $new_wr_array[$key_wr_1][] = [ - $this->convert_index_to_degree($key_wr_2), - $_tmp - ]; - } - } - - return $new_wr_array; - } - - /** - * Convert 8bit wind index direction to degree - * @param $key - * @return int - */ - function convert_index_to_degree($key): int - { - if ($key == 0) return 0; - - $_map = [ - 1 => 45, - 2 => 90, - 3 => 135, - 4 => 180, - 5 => 225, - 6 => 270, - 7 => 315 - ]; - - return $_map[$key]; - } -} \ No newline at end of file diff --git a/backend/app/Config/App.php b/backend/app/Config/App.php deleted file mode 100644 index d5bb1b0a..00000000 --- a/backend/app/Config/App.php +++ /dev/null @@ -1,465 +0,0 @@ - SYSTEMPATH, - * 'App' => APPPATH - * ]; - *``` - * @var array - */ - public $psr4 = [ - APP_NAMESPACE => APPPATH, // For custom app namespace - 'Config' => APPPATH . 'Config', - ]; - - /** - * ------------------------------------------------------------------- - * Class Map - * ------------------------------------------------------------------- - * The class map provides a map of class names and their exact - * location on the drive. Classes loaded in this manner will have - * slightly faster performance because they will not have to be - * searched for within one or more directories as they would if they - * were being autoloaded through a namespace. - * - * Prototype: - *``` - * $classmap = [ - * 'MyClass' => '/path/to/class/file.php' - * ]; - *``` - * @var array - */ - public $classmap = []; - - /** - * ------------------------------------------------------------------- - * Files - * ------------------------------------------------------------------- - * The files array provides a list of paths to __non-class__ files - * that will be autoloaded. This can be useful for bootstrap operations - * or for loading functions. - * - * Prototype: - * ``` - * $files = [ - * '/path/to/my/file.php', - * ]; - * ``` - * @var array - */ - public $files = []; -} diff --git a/backend/app/Config/Cache.php b/backend/app/Config/Cache.php deleted file mode 100644 index e5711a35..00000000 --- a/backend/app/Config/Cache.php +++ /dev/null @@ -1,167 +0,0 @@ - - */ - public $file = [ - 'storePath' => WRITEPATH . 'cache/', - 'mode' => 0640, - ]; - - /** - * ------------------------------------------------------------------------- - * Memcached settings - * ------------------------------------------------------------------------- - * Your Memcached servers can be specified below, if you are using - * the Memcached drivers. - * - * @see https://codeigniter.com/user_guide/libraries/caching.html#memcached - * - * @var array - */ - public $memcached = [ - 'host' => '127.0.0.1', - 'port' => 11211, - 'weight' => 1, - 'raw' => false, - ]; - - /** - * ------------------------------------------------------------------------- - * Redis settings - * ------------------------------------------------------------------------- - * Your Redis server can be specified below, if you are using - * the Redis or Predis drivers. - * - * @var array - */ - public $redis = [ - 'host' => '127.0.0.1', - 'password' => null, - 'port' => 6379, - 'timeout' => 0, - 'database' => 0, - ]; - - /** - * -------------------------------------------------------------------------- - * Available Cache Handlers - * -------------------------------------------------------------------------- - * - * This is an array of cache engine alias' and class names. Only engines - * that are listed here are allowed to be used. - * - * @var array - */ - public $validHandlers = [ - 'dummy' => DummyHandler::class, - 'file' => FileHandler::class, - 'memcached' => MemcachedHandler::class, - 'predis' => PredisHandler::class, - 'redis' => RedisHandler::class, - 'wincache' => WincacheHandler::class, - ]; -} diff --git a/backend/app/Config/ContentSecurityPolicy.php b/backend/app/Config/ContentSecurityPolicy.php deleted file mode 100644 index a04e3d0f..00000000 --- a/backend/app/Config/ContentSecurityPolicy.php +++ /dev/null @@ -1,167 +0,0 @@ -` element. - * - * Will default to self if not overridden - * - * @var string|string[]|null - */ - public $baseURI = null; - - /** - * Lists the URLs for workers and embedded frame contents - * - * @var string|string[] - */ - public $childSrc = 'self'; - - /** - * Limits the origins that you can connect to (via XHR, - * WebSockets, and EventSource). - * - * @var string|string[] - */ - public $connectSrc = 'self'; - - /** - * Specifies the origins that can serve web fonts. - * - * @var string|string[] - */ - public $fontSrc = null; - - /** - * Lists valid endpoints for submission from `
` tags. - * - * @var string|string[] - */ - public $formAction = 'self'; - - /** - * Specifies the sources that can embed the current page. - * This directive applies to ``, `