diff --git a/.development.env b/.development.env index d1794c7..08a0672 100644 --- a/.development.env +++ b/.development.env @@ -1,6 +1,6 @@ #Config database -DB_HOST=localhost -DB_PORT=5433 +DB_HOST=postgres +DB_PORT=5432 POSTGRES_DB=spotify POSTGRES_USER=admin POSTGRES_PASSWORD=admin123 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..60581b9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +node_modules +dist +./db/seeds/seeder diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8ae4c55 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +FROM node:20.5-alpine3.17 + +# Set the working directory to /app +WORKDIR /Spotify + +ENV ENV_NODE="development" + +COPY . . + +RUN chmod +x run_commands.sh + +# Install the app's dependencies +RUN npm install + +# Install Python 3.11 and create a virtual environment +RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python +RUN python3 -m ensurepip +RUN pip3 install --no-cache --upgrade pip setuptools + +RUN python3 -m venv venv + +RUN apk add postgresql-dev gcc python3-dev musl-dev + +# Activate the virtual environment and install the requirements +RUN . venv/bin/activate && \ + python3 -m pip install -r requirements.txt + +# Expose port 3000 for the app to listen on +EXPOSE 3000 + +# Start the app with npm run start +CMD ["npm", "run", "start:dev"] + + + diff --git a/db/seeds/seeder.py b/db/seeds/seeder.py index ba31590..5dc113b 100644 --- a/db/seeds/seeder.py +++ b/db/seeds/seeder.py @@ -5,8 +5,8 @@ # Conecta a la base de datos PostgreSQL conn = psycopg2.connect( - host="localhost", - port=5433, + host="172.29.0.2", + port=5432, database="spotify", user="admin", password="admin123" diff --git a/docker-compose.yml b/docker-compose.yml index 771fe19..3f25009 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,11 +3,20 @@ version: '3.8' services: postgres: image: postgres:16.0 - container_name: spotify_dev + container_name: spotify restart: always env_file: - .development.env volumes: - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql ports: - - 5433:5432 + - 5432:5432 + api: + build: . + ports: + - 3000:3000 + volumes: + - ./csv_files:/Spotify/csv_files + depends_on: + - postgres + diff --git a/package.json b/package.json index 178e079..5398953 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build": "nest build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "start": "export NODE_ENV=development && npm start", - "start:dev": "nest start --watch", + "start:dev": "export NODE_ENV=development && nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9c89d4e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +psycopg2-binary==2.9.9 +psycopg2==2.9.9 diff --git a/src/api/api.controller.ts b/src/api/api.controller.ts index 4bcc56d..f506916 100644 --- a/src/api/api.controller.ts +++ b/src/api/api.controller.ts @@ -16,7 +16,7 @@ export class ApiController { res.status(200).json({ "position": 1, "title": "Loudness vs. Danceability", - "type": "Scatter", + "type": "scatter", "datasets":[await this.apiService.scatterDanceabilityLoudness(artist, top, date)], "xlabel": "Danceability", "ylabel": "Loudness" @@ -33,8 +33,8 @@ export class ApiController { res.status(200).json({ "position": 2, "title": "Album Type Distribution", - "type": "Pie", - "data": await this.apiService.getAlbumTypeDistribution(artist, top, date), + "type": "pie", + "datasets": await this.apiService.getAlbumTypeDistribution(artist, top, date), "xlabel": "album type", "ylabel": "count" }); @@ -49,8 +49,8 @@ export class ApiController { res.status(200).json({ "position": 3, "title": "Artist by Track Number", - "type": "Bar", - "dataset": await this.apiService.totalTracksByArtistName(artist, top, date), + "type": "bar", + "datasets": await this.apiService.totalTracksByArtistName(artist, top, date), "xlabel": "Artist", "ylabel": "total tracks" }); @@ -65,8 +65,8 @@ export class ApiController { res.status(200).json({ "position": 4, "title": "Number of Tracks Over Time", - "type": "Line", - "dataset": await this.apiService.numberOfTracksOverTime(artist, top, date), + "type": "line", + "datasets": await this.apiService.numberOfTracksOverTime(artist, top, date), "xlabel": "Year", "ylabel": "Count" }); @@ -81,7 +81,7 @@ export class ApiController { res.status(200).json({ "position": 5, "title": "Instrumentalness vs Energy", - "type": "Scatter", + "type": "scatter", "datasets":[await this.apiService.scatterInstrumentalnessEnergy(artist, top, date)], "xlabel": "Instrumentalness", "ylabel": "Energy" diff --git a/src/api/api.service.ts b/src/api/api.service.ts index fcd35aa..d8d1521 100644 --- a/src/api/api.service.ts +++ b/src/api/api.service.ts @@ -43,11 +43,10 @@ export class ApiService { if (date) { - const year = parseInt(date); - - if (artist) query.andWhere('EXTRACT(YEAR FROM TO_DATE(release.release_date, \'YYYY-MM-DD\')) = :year', { year }); - else query.where('EXTRACT(YEAR FROM TO_DATE(release.release_date, \'YYYY-MM-DD\')) = :year', { year }); + const [startDate, endDate] = date.split('-'); + if (artist) query.andWhere("TO_DATE(release.release_date, 'YYYY-MM-DD') BETWEEN TO_DATE(:startDate, 'YYYY-MM-DD') AND TO_DATE(:endDate, 'YYYY-MM-DD')", { startDate, endDate }); + else query.where("TO_DATE(release.release_date, 'YYYY-MM-DD') BETWEEN TO_DATE(:startDate, 'YYYY-MM-DD') AND TO_DATE(:endDate, 'YYYY-MM-DD')", { startDate, endDate }); } const danceabilityLoudness = await query.getRawMany(); @@ -78,10 +77,11 @@ export class ApiService { } if (date) { - const year = parseInt(date); - if (artist) query.andWhere('EXTRACT(YEAR FROM TO_DATE(sp_release.release_date, \'YYYY-MM-DD\')) = :year', { year }); - else query.where('EXTRACT(YEAR FROM TO_DATE(release.release_date, \'YYYY-MM-DD\')) = :year', { year }); + const [startDate, endDate] = date.split('-'); + + if (artist) query.andWhere("TO_DATE(sp_release.release_date, 'YYYY-MM-DD') BETWEEN TO_DATE(:startDate, 'YYYY-MM-DD') AND TO_DATE(:endDate, 'YYYY-MM-DD')", { startDate, endDate }); + else query.where("TO_DATE(sp_release.release_date, 'YYYY-MM-DD') BETWEEN TO_DATE(:startDate, 'YYYY-MM-DD') AND TO_DATE(:endDate, 'YYYY-MM-DD')", { startDate, endDate }); } @@ -91,14 +91,12 @@ export class ApiService { const albumTypeDistribution = await query.getRawMany(); - console.log(albumTypeDistribution); - const xList = albumTypeDistribution.map(item => item.album_type); const yList = albumTypeDistribution.map(item => item.count); return { x: xList, - y: { y: yList, description: "percentage of album type" } + y: [ {y: yList, description: "percentage of album type"} ] }; } @@ -124,10 +122,10 @@ export class ApiService { if (date) { - const year = parseInt(date); + const [startDate, endDate] = date.split('-'); - if (artist) query.andWhere('EXTRACT(YEAR FROM TO_DATE(sp_release.release_date, \'YYYY-MM-DD\')) = :year', { year }); - else query.where('EXTRACT(YEAR FROM TO_DATE(sp_release.release_date, \'YYYY-MM-DD\')) = :year', { year }); + if (artist) query.andWhere("TO_DATE(sp_release.release_date, 'YYYY-MM-DD') BETWEEN TO_DATE(:startDate, 'YYYY-MM-DD') AND TO_DATE(:endDate, 'YYYY-MM-DD')", { startDate, endDate }); + else query.where("TO_DATE(sp_release.release_date, 'YYYY-MM-DD') BETWEEN TO_DATE(:startDate, 'YYYY-MM-DD') AND TO_DATE(:endDate, 'YYYY-MM-DD')", { startDate, endDate }); } const totalTracksByArtist = await query.getRawMany(); @@ -137,19 +135,18 @@ export class ApiService { return { x: xList, - y: {y: yList, "description": "total tracks of the artist"} + y: [ {y: yList, "description": "total tracks of the artist"} ] }; } async numberOfTracksOverTime(artist?: string, top?: number, date?: string) { - console.log(artist, top, date); let query = this.spotifyTrackEntity .createQueryBuilder('sp_track') - .select('sp_track.updated_on, COUNT(sp_track.track_id) as count') + .select('sp_release.release_date, COUNT(sp_track.track_id) as count') .innerJoin('sp_release', 'sp_release', 'sp_release.release_id = sp_track.release_id') - .groupBy('sp_track.updated_on'); + .groupBy('sp_release.release_date'); if (artist) { query = query.innerJoin('sp_artist_track', 'artist', 'artist.track_id = sp_track.track_id') @@ -157,12 +154,14 @@ export class ApiService { .where('sp_artist.artist_name = :artist', { artist }); } - if (date) { - const year = parseInt(date); + const [startYear, endYear] = date.split('-'); - if (artist) query.andWhere('EXTRACT(YEAR FROM TO_DATE(sp_release.release_date, \'YYYY-MM-DD\')) = :year', { year }); - else query.where('EXTRACT(YEAR FROM TO_DATE(release.release_date, \'YYYY-MM-DD\')) = :year', { year }); + if (artist) { + query.andWhere("EXTRACT(YEAR FROM TO_DATE(sp_release.release_date, 'YYYY-MM-DD')) BETWEEN :startYear AND :endYear", { startYear, endYear }); + } else { + query.andWhere("EXTRACT(YEAR FROM TO_DATE(sp_release.release_date, 'YYYY-MM-DD')) BETWEEN :startYear AND :endYear", { startYear, endYear }); + } } if (top) { @@ -171,12 +170,12 @@ export class ApiService { const tracksOverTime = await query.getRawMany(); - const xList = tracksOverTime.map(item => item.updated_on); + const xList = tracksOverTime.map(item => item.release_date); const yList = tracksOverTime.map(item => item.count); return { x: xList, - y: { y: yList, description: "tracks over time" } + y: [ {y: yList, description: "tracks over time" } ] }; } @@ -202,10 +201,10 @@ export class ApiService { if (date) { - const year = parseInt(date); + const [startDate, endDate] = date.split('-'); - if (artist) query.andWhere('EXTRACT(YEAR FROM TO_DATE(release.release_date, \'YYYY-MM-DD\')) = :year', { year }); - else query.where('EXTRACT(YEAR FROM TO_DATE(release.release_date, \'YYYY-MM-DD\')) = :year', { year }); + if (artist) query.andWhere("TO_DATE(release.release_date, 'YYYY-MM-DD') BETWEEN TO_DATE(:startDate, 'YYYY-MM-DD') AND TO_DATE(:endDate, 'YYYY-MM-DD')", { startDate, endDate }); + else query.where("TO_DATE(release.release_date, 'YYYY-MM-DD') BETWEEN TO_DATE(:startDate, 'YYYY-MM-DD') AND TO_DATE(:endDate, 'YYYY-MM-DD')", { startDate, endDate }); } @@ -235,11 +234,10 @@ export class ApiService { .getRawMany(); const xList = top10ArtistsByTrackNumber.map(item => item.artist_name); - const yList = top10ArtistsByTrackNumber.map(item => item.total); + //const yList = top10ArtistsByTrackNumber.map(item => item.total); return { - x: xList, - y: {y: yList, "description": "top 100 artists by track number"} + x: xList }; } diff --git a/src/data-retriever/data-retriever.controller.ts b/src/data-retriever/data-retriever.controller.ts index b862dfb..49ebc05 100644 --- a/src/data-retriever/data-retriever.controller.ts +++ b/src/data-retriever/data-retriever.controller.ts @@ -6,11 +6,6 @@ export class DataRetrieverController { constructor(private readonly dataRetrieverService: DataRetrieverService) {} - @Get() - async retrieveData() { - return this.dataRetrieverService.seederInit(); - } - @Get('data') async retrieveData2() { return this.dataRetrieverService.retrieveData(); diff --git a/src/data-retriever/data-retriever.service.ts b/src/data-retriever/data-retriever.service.ts index 6c71c59..71b6ef8 100644 --- a/src/data-retriever/data-retriever.service.ts +++ b/src/data-retriever/data-retriever.service.ts @@ -26,9 +26,9 @@ export class DataRetrieverService { //return this.audioRepository.find({ select: {acousticness: true} }) //return this.spotifyArtistReleaseRepository.find({ select: {artistId: true} }) //return this.spotifyReleaseRepository.find({ select: {releaseId: true, albumType: true, labelName: true} }) - //return this.spotifyArtistEntityRepository.find({ select: {artistId: true} }) + return this.spotifyArtistEntityRepository.find({ select: {artistId: true, artistName: true, id: true, updatedOn: true} }) //return this.spotifyArtistTrackEntityRepository.find({ select: {artistId: true} }) - //return this.spotifyTrackEntityRepository.find({ select: {trackId: true} }) + //return this.spotifyTrackEntityRepository.find({ select: {trackId: true, discNumber: true, durationMs: true, explicit: true, id: true, isrc: true, previewUrl: true} }) } async seederInit() { diff --git a/src/main.ts b/src/main.ts index 7460115..34382d9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,16 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); - await app.listen(3000, '0.0.0.0'); + + app.enableCors({ + + origin: true, + methods: 'GET, PUT, PATCH, POST, DELETE, OPTIONS', + credentials: true + + }) + + await app.listen(3000); console.log(`Application running on: ${await app.getUrl()}`) }