Sleep added between redmine api calls

This commit is contained in:
Pavel Gnedov 2022-02-14 01:52:04 +07:00
parent 623b62e6ff
commit 6c672cb45d
7 changed files with 95 additions and 45 deletions

View file

@ -7,5 +7,8 @@ charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[src/logic/*]
indent_size = 4
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false

View file

@ -2,5 +2,35 @@
"extends": [ "extends": [
"oclif", "oclif",
"oclif-typescript" "oclif-typescript"
] ],
"rules": {
"quotes": "off",
"indent": "off",
"semi": "off",
"unicorn/prefer-node-protocol": "off",
"unicorn/prefer-ternary": "off",
"dot-notation": "off",
"padding-line-between-statements": "off",
"camelcase": "off",
"unicorn/no-console-spaces": "off",
"new-cap": "off",
"no-await-in-loop": "off",
"unicorn/no-for-loop": "off",
"comma-dangle": "off",
"node/no-missing-import": "off",
"no-else-return": "off",
"quote-props": "off",
"no-inner-declarations": "off",
"no-process-exit": "off",
"unicorn/no-process-exit": "off",
"eol-last": "off",
"no-prototype-builtins": "off",
"padded-blocks": "off",
"unicorn/prefer-number-properties": "off",
"operator-assignment": "off",
"no-negated-condition": "off",
"unicorn/catch-error-name": "off",
"@typescript-eslint/ban-ts-comment": "off",
"unicorn/prefer-includes": "off"
}
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "redmine-time-manager", "name": "redmine-time-manager",
"version": "0.1.0", "version": "0.2.0",
"description": "Redmine Time Manager", "description": "Redmine Time Manager",
"author": "Pavel Gnedov @pavel-g", "author": "Pavel Gnedov @pavel-g",
"bin": { "bin": {

View file

@ -40,7 +40,7 @@ export const defaultConfig: ConfigTypes.Config = {
} }
}; };
export function getConfig(force: boolean = false): ConfigTypes.Config { export function getConfig(force = false): ConfigTypes.Config {
if (!force && !Args.args['config']) { if (!force && !Args.args['config']) {
return defaultConfig; return defaultConfig;
} }

View file

@ -1,14 +1,17 @@
import {config} from "./config"; import {config} from "./config";
import axios from "axios"; import axios from "axios";
export async function loadEntries(date: string): Promise<Record<string, any>> { type TimeEntry = Record<string, any>;
type TimeEntriesResponse = {time_entries: TimeEntry[]};
export async function loadEntries(date: string): Promise<TimeEntry[]> {
const url = `${config.redmine.url}/time_entries.json`; const url = `${config.redmine.url}/time_entries.json`;
const params = { const params = {
user_id: config.redmine.user_id, user_id: config.redmine.user_id,
from: date, from: date,
to: date to: date
}; };
const resp = await axios.get<Record<string, any>>(url, {params: params}); const resp = await axios.get<TimeEntriesResponse>(url, {params: params});
if (!resp || resp.status !== 200 || !resp.data || !resp.data.time_entries) { if (!resp || resp.status !== 200 || !resp.data || !resp.data.time_entries) {
throw new Error('Не удалось загрузить записи за дату'); throw new Error('Не удалось загрузить записи за дату');
} }

View file

@ -5,7 +5,7 @@ import {config} from "./config";
import axios from "axios"; import axios from "axios";
import {loadEntries} from "./open"; import {loadEntries} from "./open";
import {ColumnConverter, filterByQuery, getDate, getItemForRedmine, TimeEntryForRedmine} from "./csv"; import {ColumnConverter, filterByQuery, getDate, getItemForRedmine, TimeEntryForRedmine} from "./csv";
import {uniq} from "./utils"; import {sleep, uniq} from './utils'
function readContentFromFile(fileName: string): string { function readContentFromFile(fileName: string): string {
return fs.readFileSync(fileName, {encoding: 'utf8'}); return fs.readFileSync(fileName, {encoding: 'utf8'});
@ -13,7 +13,7 @@ function readContentFromFile(fileName: string): string {
async function readContentFromStdin(): Promise<string> { async function readContentFromStdin(): Promise<string> {
const GetStdin = await import('get-stdin'); const GetStdin = await import('get-stdin');
return await GetStdin.default(); return GetStdin.default();
} }
async function readContent(): Promise<string> { async function readContent(): Promise<string> {
@ -26,23 +26,24 @@ async function readContent(): Promise<string> {
return content; return content;
} }
function backupEntries(entries: Record<string, any>): void { function backupEntries(entries: Record<string, any>[]): void {
const fileName = `entries-${args['date']}.json`; const fileName = `entries-${args['date']}.json`;
const content = JSON.stringify(entries); const content = JSON.stringify(entries);
fs.writeFileSync(fileName, content, {encoding: "utf8"}); fs.writeFileSync(fileName, content, {encoding: "utf8"});
} }
async function deleteEntries(entries: Record<string, any>): Promise<void> { async function deleteEntries(entries: Record<string, any>[]): Promise<void> {
await Promise.all( let i: number;
entries.map(async (entry: Record<string, any>) => { for (i = 0; i < entries.length; i++) {
const url = `${config.redmine.url}/time_entries/${entry.id}.xml`; const entry = entries[i];
if (args['dry']) { const url = `${config.redmine.url}/time_entries/${entry.id}.xml`;
console.log('Delete time entry:', {url, entry}); if (args['dry']) {
} else { console.log('Delete time entry:', {url, entry});
await axios.delete(url); } else {
} await axios.delete(url);
}) }
); await sleep(100);
}
} }
async function cleanTimeEntries(items: TimeEntryForRedmine[]): Promise<void> { async function cleanTimeEntries(items: TimeEntryForRedmine[]): Promise<void> {
@ -91,6 +92,7 @@ async function saveCsv(csvData: TimeEntryForRedmine[]): Promise<void> {
} }
const saveResult = await saveItem(item); const saveResult = await saveItem(item);
if (saveResult) successCount++; if (saveResult) successCount++;
await sleep(100);
} }
console.log(`Сохранено записей: ${successCount}`); console.log(`Сохранено записей: ${successCount}`);
} }
@ -113,8 +115,14 @@ export async function save(): Promise<void> {
}) })
.map(item => item.item) as TimeEntryForRedmine[]; .map(item => item.item) as TimeEntryForRedmine[];
if (args['rewrite']) await cleanTimeEntries(items); if (args['rewrite']) {
console.log('Очистка существующих записей...')
await cleanTimeEntries(items);
console.log('Очистка существующих записей завершена')
}
console.log('Сохранение новых записей...');
await saveCsv(items); await saveCsv(items);
console.log('Сохранение новых записей завершено');
} }
export function getUniqDates(items: TimeEntryForRedmine[]): string[] { export function getUniqDates(items: TimeEntryForRedmine[]): string[] {

View file

@ -1,34 +1,40 @@
export function invert(obj: Record<any, any>): Record<any, any> { export function invert(obj: Record<any, any>): Record<any, any> {
const newObj = {}; const newObj = {}
let key; let key
for (key in obj) { for (key in obj) {
if (!obj.hasOwnProperty(key)) continue; if (!obj.hasOwnProperty(key)) continue
const value = obj[key]; const value = obj[key]
// @ts-ignore // @ts-ignore
newObj[value] = key; newObj[value] = key
} }
return newObj; return newObj
} }
export function uniq<T>(src: T[]): T[] { export function uniq<T>(src: T[]): T[] {
const res = []; const res = []
let i: number; let i: number
for (i = 0; i < src.length; i++) { for (i = 0; i < src.length; i++) {
const value = src[i]; const value = src[i]
if (res.indexOf(value) < 0) { if (res.indexOf(value) < 0) {
res.push(value); res.push(value)
}
} }
} return res
return res;
} }
export function assign(target: Record<string, any>, src: Record<string, any>): Record<string, any> { export function assign(target: Record<string, any>, src: Record<string, any>): Record<string, any> {
let key: string; let key: string
for (key in src) { for (key in src) {
if (src.hasOwnProperty(key)) { if (src.hasOwnProperty(key)) {
console.debug('rewrite key:', key, ' with value:', src[key]); // DEBUG console.debug('rewrite key:', key, ' with value:', src[key]) // DEBUG
target[key] = src[key]; target[key] = src[key]
}
} }
} return target
return target; }
export async function sleep(timeout: number): Promise<void> {
return new Promise(resolve => {
setTimeout(resolve, timeout)
});
} }