Skip to content

Commit

Permalink
rdy task
Browse files Browse the repository at this point in the history
  • Loading branch information
slava560730 committed Nov 24, 2024
1 parent 9f00eb6 commit 1b71266
Show file tree
Hide file tree
Showing 19 changed files with 358 additions and 0 deletions.
2 changes: 2 additions & 0 deletions mocks/data.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Nice, cozy, warm big bed apartment Design interior in most sympathetic area! Complitely renovated, well-equipped, cosy studio in idyllic, over 100 years old wooden house. Calm street, fast connection to center and airport. 2024-04-07T08:45:40.283Z Amsterdam https://16.design.htmlacademy.pro/static/hotel/17.jpg https://16.design.htmlacademy.pro/static/hotel/16.jpg;https://16.design.htmlacademy.pro/static/hotel/13.jpg;https://16.design.htmlacademy.pro/static/hotel/11.jpg;https://16.design.htmlacademy.pro/static/hotel/20.jpg;https://16.design.htmlacademy.pro/static/hotel/6.jpg;https://16.design.htmlacademy.pro/static/hotel/8.jpg true false 2,4 house 5 7 709 Towels;Laptop friendly workspace;Baby seat Ангелина [email protected] https://16.design.htmlacademy.pro/static/host/avatar-angelina.jpg password1 ordinary 10 48.868610000000004;2.342499
Tile House Peaceful studio in the most wanted area in town. Quiet house Near of everything. Completely renovated. Lovely neighbourhood, lot of trendy shops, restaurants and bars in a walking distance. 2023-05-02T08:45:40.283Z Brussels https://16.design.htmlacademy.pro/static/hotel/20.jpg https://16.design.htmlacademy.pro/static/hotel/16.jpg;https://16.design.htmlacademy.pro/static/hotel/13.jpg;https://16.design.htmlacademy.pro/static/hotel/11.jpg;https://16.design.htmlacademy.pro/static/hotel/20.jpg;https://16.design.htmlacademy.pro/static/hotel/6.jpg;https://16.design.htmlacademy.pro/static/hotel/8.jpg true false 4,5 house 6 8 152 Fridge;Towels;Laptop friendly workspace;Washer Екатерина [email protected] https://16.design.htmlacademy.pro/static/host/avatar-angelina.jpg password2 pro 10 50.950361;6.961974
40 changes: 40 additions & 0 deletions src/cli/cli-application.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Command } from './commands/command.interface.js';
import {CommandParser} from "./command-parser.js";

Check failure on line 2 in src/cli/cli-application.ts

View workflow job for this annotation

GitHub Actions / Check

Strings must use singlequote

type CommandCollection = Record<string, Command>;

export class CLIApplication {
private commands: CommandCollection = {};

constructor(
private readonly defaultCommand: string = '--help'
) {}

public registerCommands(commandList: Command[]): void {
commandList.forEach((command) => {
if (Object.hasOwn(this.commands, command.getName())) {
throw new Error(`Command ${command.getName()} is already registered`);
}
this.commands[command.getName()] = command;
});
}

public getCommand(commandName: string): Command {
return this.commands[commandName] ?? this.getDefaultCommand();
}

public getDefaultCommand(): Command | never {
if (! this.commands[this.defaultCommand]) {
throw new Error(`The default command (${this.defaultCommand}) is not registered.`);
}
return this.commands[this.defaultCommand];
}

public processCommand(argv: string[]): void {
const parsedCommand = CommandParser.parse(argv);
const [commandName] = Object.keys(parsedCommand);
const command = this.getCommand(commandName);
const commandArguments = parsedCommand[commandName] ?? [];
command.execute(...commandArguments);
}
}
19 changes: 19 additions & 0 deletions src/cli/command-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
type ParsedCommand = Record<string, string[]>

export class CommandParser {
static parse(cliArguments: string[]): ParsedCommand {
const parsedCommand: ParsedCommand = {};
let currentCommand = '';

for (const argument of cliArguments) {
if (argument.startsWith('--')) {
parsedCommand[argument] = [];
currentCommand = argument;
} else if (currentCommand && argument) {
parsedCommand[currentCommand].push(argument);
}
}

return parsedCommand;
}
}
5 changes: 5 additions & 0 deletions src/cli/commands/command.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface Command {
getName(): string;

execute(...parameters: string[]): void;
}
20 changes: 20 additions & 0 deletions src/cli/commands/help.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Command } from './command.interface.js';

export class HelpCommand implements Command {
public getName(): string {
return '--help';
}

public async execute(..._parameters: string[]): Promise<void> {
console.info(`
Программа для подготовки данных для REST API сервера.
Пример:
cli.js --<command> [--arguments]
Команды:
--version: # выводит номер версии
--help: # печатает этот текст
--import <path>: # импортирует данные из TSV
--generate <n> <path> <url> # генерирует произвольное количество тестовых данных
`);
}
}
26 changes: 26 additions & 0 deletions src/cli/commands/import.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Command } from './command.interface.js';
import {TSVFileReader} from "../../shared/libs/file-reader/index.js";

Check failure on line 2 in src/cli/commands/import.command.ts

View workflow job for this annotation

GitHub Actions / Check

Strings must use singlequote

export class ImportCommand implements Command {
public getName(): string {
return '--import';
}

public execute(...parameters: string[]): void {
const [filename] = parameters;
const fileReader = new TSVFileReader(filename.trim());

try {
fileReader.read();
console.log(fileReader.toArray());
} catch (err) {

if (!(err instanceof Error)) {
throw err;
}

console.error(`Can't import data from file: ${filename}`);
console.error(`Details: ${err.message}`);
}
}
}
51 changes: 51 additions & 0 deletions src/cli/commands/version.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';

import { Command } from './command.interface.js';

type PackageJSONConfig = {
version: string;
}

function isPackageJSONConfig(value: unknown): value is PackageJSONConfig {
return (
typeof value === 'object' &&
value !== null &&
!Array.isArray(value) &&
Object.hasOwn(value, 'version')
);
}

export class VersionCommand implements Command {
constructor(
private readonly filePath: string = 'package.json'
) {}

private readVersion(): string {
const jsonContent = readFileSync(resolve(this.filePath), 'utf-8');
const importedContent: unknown = JSON.parse(jsonContent);

if (! isPackageJSONConfig(importedContent)) {
throw new Error('Failed to parse json content.');
}

return importedContent.version;
}

public getName(): string {
return '--version';
}

public async execute(..._parameters: string[]): Promise<void> {
try {
const version = this.readVersion();
console.info(version);
} catch (error: unknown) {
console.error(`Failed to read version from ${this.filePath}`);

if (error instanceof Error) {
console.error(error.message);
}
}
}
}
6 changes: 6 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { CLIApplication } from './cli-application.js';
export { CommandParser } from './command-parser.js';

export { HelpCommand } from './commands/help.command.js';
export { VersionCommand } from './commands/version.command.js';
export { ImportCommand } from './commands/import.command.js';
15 changes: 15 additions & 0 deletions src/main.cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env node
import { CLIApplication, HelpCommand, VersionCommand, ImportCommand } from './cli/index.js';

function bootstrap() {
const cliApplication = new CLIApplication();
cliApplication.registerCommands([
new HelpCommand(),
new VersionCommand(),
new ImportCommand(),
]);

cliApplication.processCommand(process.argv);
}

bootstrap();
3 changes: 3 additions & 0 deletions src/shared/libs/file-reader/file-reader.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface FileReader {
read(): void;
}
2 changes: 2 additions & 0 deletions src/shared/libs/file-reader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { FileReader } from './file-reader.interface.js';
export { TSVFileReader } from './tsv-file-reader.js';
99 changes: 99 additions & 0 deletions src/shared/libs/file-reader/tsv-file-reader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { readFileSync } from 'node:fs';

import { FileReader } from './file-reader.interface.js';
import { Offer, City, OfferType, Goods, User, Location } from '../../types/index.js';
import { TypeUser } from '../../types/user.type.js';


export class TSVFileReader implements FileReader {
private rawData = '';

constructor(
private readonly filename: string
) {}

private validateRawData(): void {
if (!this.rawData) {
throw new Error('File was not read');
}
}

private parseRawDataToOffers(): Offer[] {
return this.rawData
.split('\n')
.filter((row) => row.trim().length > 0)
.map((line) => this.parseLineToOffer(line));
}

private parseLineToOffer(line: string): Offer {
const [
title,
description,
postDate,
city,
previewImage,
images,
isPremium,
isFavorite,
rating,
type,
bedrooms,
maxAdults,
price,
goods,
firstname,
email,
avatarPath,
password,
typeUser,
reviewsCount,
location
] = line.split('\t');

return {
title,
description,
postDate: new Date(postDate),
city: city as City,
previewImage,
images:this.parseImages(images),
isPremium: isPremium === 'true',
isFavorite: isFavorite === 'true',
rating: Number.parseInt(rating, 10),
type:type as OfferType,
bedrooms:Number.parseInt(bedrooms,10),
maxAdults:Number.parseInt(maxAdults,10),
price: Number.parseInt(price, 10),
goods: this.parseGoods(goods),
user: this.parseUser(firstname, email, avatarPath, password, typeUser as TypeUser),
reviewsCount:Number.parseInt(reviewsCount,10),
location: this.parseLocation(location)
};
}

private parseGoods(goodsString: string): Goods[] {
return goodsString.split(';').map((name) => name as Goods);
}

private parseLocation(locationString: string): Location {
const [latitude, longitude] = locationString.split(';').map((name) => name);
return {latitude:Number.parseFloat(latitude),longitude:Number.parseFloat(longitude)} ;
}

private parseImages(imagesString: string): string[] {
return imagesString.split(';').map((name) => name);
}

private parseUser(firstname: string, email: string, avatarPath: string, password: string,type: TypeUser): User {
return { firstname, email, avatarPath, password, type };
}

public read(): void {
this.rawData = readFileSync(this.filename, { encoding: 'utf-8' });
}

public toArray(): Offer[] {
this.validateRawData();
return this.parseRawDataToOffers();
}
}
8 changes: 8 additions & 0 deletions src/shared/types/city.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export enum City {
Paris = 'Paris',
Cologne = 'Cologne',
Brussels = 'Brussels',
Amsterdam = 'Amsterdam',
Hamburg = 'Hamburg',
Dusseldorf = 'Dusseldorf',
}
9 changes: 9 additions & 0 deletions src/shared/types/goods.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export enum Goods {
Breakfast = 'Breakfast',
AirConditioning = 'Air conditioning',
Laptop = 'Laptop friendly workspace',
Baby = 'Baby seat',
Washer = 'Washer',
Towels = 'Towels',
Fridge = 'Fridge'
}
6 changes: 6 additions & 0 deletions src/shared/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { Offer} from './offer.type.js';
export { City } from './city.type.js';
export { Goods } from './goods.type.js';
export { Location } from './location.type.js';
export { OfferType } from './offer-type.type.js';
export { User } from './user.type.js';
4 changes: 4 additions & 0 deletions src/shared/types/location.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type Location = {
latitude: number;
longitude: number;
}
6 changes: 6 additions & 0 deletions src/shared/types/offer-type.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum OfferType {
Apartment = 'Apartment',
House = 'House',
Room = 'Room',
Hotel = 'Hotel',
}
27 changes: 27 additions & 0 deletions src/shared/types/offer.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { City } from './city.type.js';
import { Goods } from './goods.type.js';
import { Location } from './location.type.js';
import { OfferType } from './offer-type.type.js';
import { User } from './user.type.js';

export type OfferPreview = {
title: string;//наименование
type: OfferType;//тип жилья
price: number;//стоимость аренды
city: City;//город
location: Location;//координаты предложения
isFavorite: boolean;//флаг избранное
isPremium: boolean;//флаг премиум
rating: number;//рейтинг
previewImage: string;//превью изображения
};
export type Offer = OfferPreview & {
description: string;//описание
postDate:Date;//дата публикации
bedrooms: number;//количество комнат
goods: Goods[];//удобства
user: User;//пользователь
images: string[];//фотографии жилья
maxAdults: number;//Количество гостей
reviewsCount :number;//количество отзывов
};
10 changes: 10 additions & 0 deletions src/shared/types/user.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type TypeUser = 'ordinary' | 'pro';

export type User = {
firstname: string;
email: string;
avatarPath: string;
password: string;
type:TypeUser;

}

0 comments on commit 1b71266

Please sign in to comment.