Skip to content

Commit

Permalink
finished Data layer of Intraday and CompanyInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
LeonelZalegas committed Jul 22, 2024
1 parent d920370 commit 7307abf
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 12 deletions.
7 changes: 2 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,7 @@ dependencies {

// Retrofit
implementation(libs.retrofit2.retrofit)
implementation(libs.retrofit2.converter.moshi)

// Moshi
implementation(libs.moshi.kotlin)
ksp(libs.moshi.kotlin.codegen)
implementation(libs.converter.gson)

// OkHttp
implementation(libs.okhttp3.okhttp)
Expand All @@ -161,5 +157,6 @@ dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.coroutines.android)

// System Bar control
implementation(libs.accompanist.systemuicontroller)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.create
import javax.inject.Singleton

Expand All @@ -20,6 +21,7 @@ object AppModule {
fun provideStockApi(): StockClient {
return Retrofit.Builder()
.baseUrl("https://alphavantage.co")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package com.example.stockmarketcheck.di

import com.example.stockmarketcheck.mainFeature.data.csv.CSVParser
import com.example.stockmarketcheck.mainFeature.data.csv.CompanyListingsParser
import com.example.stockmarketcheck.mainFeature.data.csv.IntradayInfoParser
import com.example.stockmarketcheck.mainFeature.data.repository.StockRepositoryImpl
import com.example.stockmarketcheck.mainFeature.domain.model.CompanyListing
import com.example.stockmarketcheck.mainFeature.domain.model.IntradayInfo
import com.example.stockmarketcheck.mainFeature.domain.repository.StockRepository
import dagger.Binds
import dagger.Module
Expand All @@ -18,6 +20,10 @@ abstract class RepositoryModule {
@Singleton
abstract fun bindCompanyListingsParser(companyListingsParser: CompanyListingsParser): CSVParser<CompanyListing>

@Binds
@Singleton
abstract fun bindIntradayInfoParser(intradayInfoParser: IntradayInfoParser): CSVParser<IntradayInfo>

@Binds
@Singleton
abstract fun bindStockRepository(stockRepositoryImpl: StockRepositoryImpl): StockRepository
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.example.stockmarketcheck.mainFeature.data.csv

import android.os.Build
import androidx.annotation.RequiresApi
import com.example.stockmarketcheck.mainFeature.domain.model.IntradayInfo
import com.opencsv.CSVReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.io.InputStreamReader
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.Locale
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class IntradayInfoParser
@Inject
constructor() : CSVParser<IntradayInfo> {
@RequiresApi(Build.VERSION_CODES.O)
override suspend fun parse(stream: InputStream): List<IntradayInfo> {
val csvReader = CSVReader(InputStreamReader(stream))
return withContext(Dispatchers.IO) {
csvReader
.readAll()
.drop(1)
.mapNotNull { line ->
val timestamp = line.getOrNull(0) ?: return@mapNotNull null
val close = line.getOrNull(4) ?: return@mapNotNull null
IntradayInfo(
date = convertToLocalDateTime(timestamp),
close = close.toDouble(),
)
} // Aca ya tenemos la lista de IntradayInfo pero a esto filtramos y Guardamos solo
.filter { // los IntradayInfo con campo date = a la fecha de ayer de nuetra maquina
it.date.dayOfMonth == LocalDate.now().minusDays(4).dayOfMonth
}
.sortedBy {
it.date.hour
}
.also {
csvReader.close()
}
}
}

@RequiresApi(Build.VERSION_CODES.O)
private fun convertToLocalDateTime(timestamp: String): LocalDateTime {
val pattern = "yyyy-MM-dd HH:mm:ss"
val formatter = DateTimeFormatter.ofPattern(pattern, Locale.getDefault())
val localDateTime = LocalDateTime.parse(timestamp, formatter)
return localDateTime
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.example.stockmarketcheck.mainFeature.data.mapper

import com.example.stockmarketcheck.mainFeature.data.local.CompanyListingEntity
import com.example.stockmarketcheck.mainFeature.data.remote.dto.CompanyInfoDto
import com.example.stockmarketcheck.mainFeature.domain.model.CompanyInfo
import com.example.stockmarketcheck.mainFeature.domain.model.CompanyListing

fun CompanyListingEntity.toCompanyListing(): CompanyListing {
Expand All @@ -17,4 +19,17 @@ fun CompanyListing.toCompanyListingEntity(): CompanyListingEntity {
symbol = symbol,
exchange = exchange,
)
}

fun CompanyInfoDto.toCompanyInfo(): CompanyInfo {
// ponemos ?: "" xq puede ser q te pases de llamadas gratuitas y la API te devuelva
// otro JSON q no corresponde con los fields de CompanyInfoDto (no existen) x ende
// mejor que devuelva "" a q crashee
return CompanyInfo(
symbol = symbol ?: "",
description = description ?: "",
name = name ?: "",
country = country ?: "",
industry = industry ?: "",
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.stockmarketcheck.mainFeature.data.remote

import com.example.stockmarketcheck.BuildConfig
import com.example.stockmarketcheck.mainFeature.data.remote.dto.CompanyInfoDto
import okhttp3.ResponseBody
import retrofit2.http.GET
import retrofit2.http.Query
Expand All @@ -10,4 +11,16 @@ interface StockClient {
suspend fun getListings(
@Query("apikey") apikey: String = BuildConfig.API_KEY,
): ResponseBody

@GET("query?function=TIME_SERIES_INTRADAY&interval=60min&datatype=csv")
suspend fun getIntradayInfo(
@Query("symbol") symbol: String,
@Query("apikey") apiKey: String = BuildConfig.API_KEY,
): ResponseBody

@GET("query?function=OVERVIEW")
suspend fun getCompanyInfo(
@Query("symbol") symbol: String,
@Query("apikey") apiKey: String = BuildConfig.API_KEY,
): CompanyInfoDto
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example.stockmarketcheck.mainFeature.data.remote.dto

import com.google.gson.annotations.SerializedName

// Seria el "Response" que llama a aristidevs y es el que sera rellenado x ConverterFactoryJson
data class CompanyInfoDto(
@SerializedName("Symbol") val symbol: String?,
@SerializedName("Description") val description: String?,
@SerializedName("Name") val name: String?,
@SerializedName("Country") val country: String?,
@SerializedName("Industry") val industry: String?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package com.example.stockmarketcheck.mainFeature.data.repository

import com.example.stockmarketcheck.mainFeature.data.csv.CSVParser
import com.example.stockmarketcheck.mainFeature.data.local.StockDatabase
import com.example.stockmarketcheck.mainFeature.data.mapper.toCompanyInfo
import com.example.stockmarketcheck.mainFeature.data.mapper.toCompanyListing
import com.example.stockmarketcheck.mainFeature.data.mapper.toCompanyListingEntity
import com.example.stockmarketcheck.mainFeature.data.remote.StockClient
import com.example.stockmarketcheck.mainFeature.domain.model.CompanyInfo
import com.example.stockmarketcheck.mainFeature.domain.model.CompanyListing
import com.example.stockmarketcheck.mainFeature.domain.model.IntradayInfo
import com.example.stockmarketcheck.mainFeature.domain.repository.StockRepository
import com.example.stockmarketcheck.util.Resource
import kotlinx.coroutines.flow.Flow
Expand All @@ -22,6 +25,7 @@ class StockRepositoryImpl
private val api: StockClient,
db: StockDatabase,
private val companyListingsParser: CSVParser<CompanyListing>,
private val intradayInfoParser: CSVParser<IntradayInfo>,
) : StockRepository {
private val dao = db.dao

Expand Down Expand Up @@ -81,4 +85,44 @@ class StockRepositoryImpl
}
}
}

override suspend fun getIntradayInfo(symbol: String): Resource<List<IntradayInfo>> {
return try {
val response = api.getIntradayInfo(symbol)
val results = intradayInfoParser.parse(response.byteStream())
Resource.Success(results)
} catch (e: IOException) {
e.printStackTrace()
Resource.Error(
message = "Couldn't load intraday info",
null,
)
} catch (e: HttpException) {
e.printStackTrace()
Resource.Error(
message = "Couldn't load intraday info",
null,
)
}
}

// basicamente el unico donde hacemos el parseo Json posta
override suspend fun getCompanyInfo(symbol: String): Resource<CompanyInfo> {
return try {
val result = api.getCompanyInfo(symbol)
Resource.Success(result.toCompanyInfo())
} catch (e: IOException) {
e.printStackTrace()
Resource.Error(
message = "Couldn't load company info",
null,
)
} catch (e: HttpException) {
e.printStackTrace()
Resource.Error(
message = "Couldn't load company info",
null,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.stockmarketcheck.mainFeature.domain.model

data class CompanyInfo(
val symbol: String?,
val description: String?,
val name: String?,
val country: String?,
val industry: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.stockmarketcheck.mainFeature.domain.model

import java.time.LocalDateTime

data class IntradayInfo(
val date: LocalDateTime,
val close: Double,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.example.stockmarketcheck.mainFeature.domain.repository

import com.example.stockmarketcheck.mainFeature.domain.model.CompanyInfo
import com.example.stockmarketcheck.mainFeature.domain.model.CompanyListing
import com.example.stockmarketcheck.mainFeature.domain.model.IntradayInfo
import com.example.stockmarketcheck.util.Resource
import kotlinx.coroutines.flow.Flow

Expand All @@ -9,4 +11,8 @@ interface StockRepository {
fetchFromRemote: Boolean,
query: String,
): Flow<Resource<List<CompanyListing>>>

suspend fun getIntradayInfo(symbol: String): Resource<List<IntradayInfo>>

suspend fun getCompanyInfo(symbol: String): Resource<CompanyInfo>
}
9 changes: 2 additions & 7 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[versions]
accompanistSystemuicontroller = "0.28.0"
agp = "8.5.1"
converterGson = "2.11.0"
kotlin = "1.9.0"
coreKtx = "1.13.1"
junit = "4.13.2"
Expand All @@ -14,8 +15,6 @@ composeBom = "2024.06.00"
composeNavigation = "2.8.0-beta05"
material = "1.6.8"
materialIconsExtended = "<latest_compose_version>"
moshiKotlin = "1.15.0"
moshiKotlinCodegen = "1.15.0"
okHttpVersion = "4.12.0"
opencsv = "5.7.1"
retrofitVersion = "2.10.0"
Expand All @@ -24,7 +23,6 @@ serialization = "1.6.1"
ksp = "1.9.0-1.0.13"
hilt = "2.51"
hiltNavigationCompose = "1.2.0"
hiltLifecycleViewmodel = "1.0.0-alpha03"

[libraries]
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
Expand All @@ -36,6 +34,7 @@ androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref =
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
androidx-room-testing = { module = "androidx.room:room-testing", version.ref = "roomRuntime" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
Expand All @@ -51,8 +50,6 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshiKotlin" }
moshi-kotlin-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshiKotlinCodegen" }
navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "composeNavigation" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization"}
okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okHttpVersion" }
Expand All @@ -61,8 +58,6 @@ opencsv = { module = "com.opencsv:opencsv", version.ref = "opencsv" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
hilt-lifecycle-viewmodel = { module = "androidx.hilt:hilt-lifecycle-viewmodel", version.ref = "hiltLifecycleViewmodel" }
retrofit2-converter-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofitVersion" }
retrofit2-retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofitVersion" }

[plugins]
Expand Down

0 comments on commit 7307abf

Please sign in to comment.