Skip to content

Commit

Permalink
[#6] mountain data seeding
Browse files Browse the repository at this point in the history
  • Loading branch information
le2sky committed Mar 6, 2022
1 parent 6d19de3 commit d6d7e5d
Show file tree
Hide file tree
Showing 13 changed files with 439 additions and 7 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ lerna-debug.log*
!.vscode/launch.json
!.vscode/extensions.json

**/.env
**/.env

src/public/data/**/*.json
10 changes: 9 additions & 1 deletion nest-cli.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
{
"collection": "@nestjs/schematics",
"sourceRoot": "src"
"sourceRoot": "src",
"compilerOptions": {
"assets": [
{
"include": "./public/data/result/*.json",
"outDir": "./dist"
}
]
}
}
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
"@nestjs/platform-express": "^8.0.0",
"@nestjs/swagger": "^5.2.0",
"@nestjs/typeorm": "^8.0.3",
"axios": "^0.26.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"cross-env": "^7.0.3",
"dotenv": "^16.0.0",
"express-basic-auth": "^1.2.1",
"firebase-admin": "^10.0.1",
"joi": "^17.6.0",
Expand Down
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { validationSchema } from './config/validationSchema';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import { UserModule } from './user/user.module';
import { PostModule } from './post/post.module';
import { MountainModule } from './mountain/mountain.module';

@Module({
imports: [
Expand All @@ -28,6 +29,7 @@ import { PostModule } from './post/post.module';
}),
UserModule,
PostModule,
MountainModule,
],
})
export class AppModule {}
4 changes: 2 additions & 2 deletions src/database/entities/mountains.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export class MountainsEntity extends DateAuditEntity {
@Column()
url: string;

@Column()
@Column({ type: 'numeric' })
lat: number;

@Column()
@Column({ type: 'numeric' })
lon: number;

@Column('int', { name: 'imageId', nullable: true })
Expand Down
129 changes: 129 additions & 0 deletions src/lib/mountain-generator/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
const REGION_BASE = require('./region_base');

const axios = require('axios');
const fs = require('fs-extra');

const path = require('path');
require('dotenv').config();

const targetPath = path.join(__dirname, '..', '..');

const KAKAO_REST_API_KEY = process.env.KAKAO_REST_API_KEY;

const updatedAt = Date.now();

let totalDataCount = 0;

const resultMap = new Map();

const uniquenessMap = new Map();

const regionKeys = Object.keys(REGION_BASE);

const getTargetPath = (region) => `${targetPath}/public/data/result/${region}.json`;

const TARGET_DIR = `${targetPath}/public/data/result`;

const LOCATION_PATH = `${targetPath}/public/data/result/location.json`;

const RESULT_PATH = `${targetPath}/public/data/result/total_count.txt`;

const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));

// ๊ฒ€์ƒ‰์–ด, ์นดํ…Œ๊ณ ๋ฆฌ //
const QUERY = '์‚ฐ';
const CATEGORY_NAME = '์—ฌํ–‰ > ๊ด€๊ด‘,๋ช…์†Œ > ์‚ฐ';

// ex) ['์„œ์šธํŠน๋ณ„์‹œ ์ข…๋กœ๊ตฌ', ...];
const REGION = Object.entries(REGION_BASE).reduce((acc, [k, v]) => [...acc, ...v.map((u) => `${k} ${u}`)], []);

const kakaoSearchAPI = ({ query, page = 1 }) =>
axios.get(`https://dapi.kakao.com/v2/local/search/keyword.json`, {
headers: {
Authorization: `KakaoAK ${KAKAO_REST_API_KEY}`,
},
params: {
query,
page,
},
timeout: 10000,
});

async function* asyncAPI(query, total_pages) {
for (let i = 1; i <= total_pages; i++) {
const { data } = await kakaoSearchAPI({ query, page: i });
await sleep(300);
yield data;
}
}

const fetchRegionData = async () => {
for (const region of REGION) {
const query = `${region} ${QUERY}`;
const regionKey = region.split(' ')[0];
const targetRegionArray = resultMap.get(regionKey);

const {
data: { meta },
} = await kakaoSearchAPI({ query });

const { pageable_count, total_count } = meta;

if (pageable_count * 15 < total_count) {
throw new Error(`${region} ์ง€์—ญ ์ •๋ณด๋Š” ๋” ์ƒ์„ธํ•œ ๊ฒ€์ƒ‰์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค`);
}

for await (const { documents } of asyncAPI(query, pageable_count)) {
documents.forEach((v) => {
const isDuplicatePresent = uniquenessMap.has(v.id);
if (isDuplicatePresent) return;
uniquenessMap.set(v.id, true);

const isCategoryMatch = v.category_name !== CATEGORY_NAME;
if (isCategoryMatch) return;

targetRegionArray.push(v);
});
}

console.log(`${region} ์ง€์—ญ ๋ฐ์ดํ„ฐ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.`);
}
};

const writeRegionData = () => {
for (const regionKey of regionKeys) {
const regionResultArray = resultMap.get(regionKey);

totalDataCount += regionResultArray.length;

fs.writeFileSync(getTargetPath(regionKey), JSON.stringify(regionResultArray));
}
};

const writeResult = () => {
const endAt = new Date().getTime();
fs.writeFileSync(RESULT_PATH, `totalDataCount : ${totalDataCount} time: ${(endAt - updatedAt) / 1000}`);
fs.writeFileSync(LOCATION_PATH, JSON.stringify(REGION_BASE));
};

(async () => {
if (!fs.existsSync(TARGET_DIR)) {
fs.mkdirSync(TARGET_DIR);
}

for (const regionKey of regionKeys) {
resultMap.set(regionKey, []);
}

try {
await fetchRegionData();

writeRegionData();

writeResult();
console.log('๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ ํ•˜์˜€์Šต๋‹ˆ๋‹ค');
} catch (error) {
console.warn('์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ค‘ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ ํ–ˆ์Šต๋‹ˆ๋‹ค', error);
fs.rmdirSync(TARGET_DIR, { recursive: true });
}
})();
Loading

0 comments on commit d6d7e5d

Please sign in to comment.