Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-21 15:11:39 +01:00
parent d6b5d53060
commit d90a1dc8df
2145 changed files with 210227 additions and 2 deletions

1
data/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

31
data/build.gradle.kts Normal file
View file

@ -0,0 +1,31 @@
plugins {
id("breezy.library")
kotlin("android")
kotlin("plugin.serialization")
id("app.cash.sqldelight")
}
android {
namespace = "breezyweather.data"
defaultConfig {
consumerProguardFiles("consumer-rules.pro")
}
sqldelight {
databases {
create("Database") {
packageName.set("breezyweather.data")
dialect(libs.sqldelight.dialects.sql)
schemaOutputDirectory.set(project.file("./src/main/sqldelight"))
}
}
}
}
dependencies {
implementation(projects.domain)
implementation(projects.weatherUnit)
api(libs.bundles.sqldelight)
}

0
data/consumer-rules.pro Normal file
View file

21
data/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View file

@ -0,0 +1,107 @@
/**
* This file is part of Breezy Weather.
*
* Breezy Weather is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Breezy Weather is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Breezy Weather. If not, see <https://www.gnu.org/licenses/>.
*/
package breezyweather.data
import app.cash.sqldelight.ExecutableQuery
import app.cash.sqldelight.Query
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import app.cash.sqldelight.coroutines.mapToOne
import app.cash.sqldelight.coroutines.mapToOneOrNull
import app.cash.sqldelight.db.SqlDriver
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
class AndroidDatabaseHandler(
val db: Database,
private val driver: SqlDriver,
val queryDispatcher: CoroutineDispatcher = Dispatchers.IO,
val transactionDispatcher: CoroutineDispatcher = queryDispatcher,
) : DatabaseHandler {
val suspendingTransactionId = ThreadLocal<Int>()
override suspend fun <T> await(inTransaction: Boolean, block: suspend Database.() -> T): T {
return dispatch(inTransaction, block)
}
override suspend fun <T : Any> awaitList(
inTransaction: Boolean,
block: suspend Database.() -> Query<T>,
): List<T> {
return dispatch(inTransaction) { block(db).executeAsList() }
}
override suspend fun <T : Any> awaitOne(
inTransaction: Boolean,
block: suspend Database.() -> Query<T>,
): T {
return dispatch(inTransaction) { block(db).executeAsOne() }
}
override suspend fun <T : Any> awaitOneExecutable(
inTransaction: Boolean,
block: suspend Database.() -> ExecutableQuery<T>,
): T {
return dispatch(inTransaction) { block(db).executeAsOne() }
}
override suspend fun <T : Any> awaitOneOrNull(
inTransaction: Boolean,
block: suspend Database.() -> Query<T>,
): T? {
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
}
override suspend fun <T : Any> awaitOneOrNullExecutable(
inTransaction: Boolean,
block: suspend Database.() -> ExecutableQuery<T>,
): T? {
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
}
override fun <T : Any> subscribeToList(block: Database.() -> Query<T>): Flow<List<T>> {
return block(db).asFlow().mapToList(queryDispatcher)
}
override fun <T : Any> subscribeToOne(block: Database.() -> Query<T>): Flow<T> {
return block(db).asFlow().mapToOne(queryDispatcher)
}
override fun <T : Any> subscribeToOneOrNull(block: Database.() -> Query<T>): Flow<T?> {
return block(db).asFlow().mapToOneOrNull(queryDispatcher)
}
private suspend fun <T> dispatch(inTransaction: Boolean, block: suspend Database.() -> T): T {
// Create a transaction if needed and run the calling block inside it.
if (inTransaction) {
return withTransaction { block(db) }
}
// If we're currently in the transaction thread, there's no need to dispatch our query.
if (driver.currentTransaction() != null) {
return block(db)
}
// Get the current database context and run the calling block.
val context = getCurrentDatabaseContext()
return withContext(context) { block(db) }
}
}

View file

@ -0,0 +1,132 @@
/**
* This file is part of Breezy Weather.
*
* Breezy Weather is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Breezy Weather is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Breezy Weather. If not, see <https://www.gnu.org/licenses/>.
*/
package breezyweather.data
import app.cash.sqldelight.ColumnAdapter
import breezyweather.domain.weather.reference.AlertSeverity
import breezyweather.domain.weather.reference.WeatherCode
import org.breezyweather.unit.distance.Distance
import org.breezyweather.unit.distance.Distance.Companion.meters
import org.breezyweather.unit.pollen.PollenConcentration
import org.breezyweather.unit.pollen.PollenConcentration.Companion.perCubicMeter
import org.breezyweather.unit.pollutant.PollutantConcentration
import org.breezyweather.unit.pollutant.PollutantConcentration.Companion.microgramsPerCubicMeter
import org.breezyweather.unit.precipitation.Precipitation
import org.breezyweather.unit.precipitation.Precipitation.Companion.micrometers
import org.breezyweather.unit.pressure.Pressure
import org.breezyweather.unit.pressure.Pressure.Companion.pascals
import org.breezyweather.unit.ratio.Ratio
import org.breezyweather.unit.ratio.Ratio.Companion.permille
import org.breezyweather.unit.speed.Speed
import org.breezyweather.unit.speed.Speed.Companion.centimetersPerSecond
import org.breezyweather.unit.temperature.Temperature
import org.breezyweather.unit.temperature.Temperature.Companion.deciCelsius
import java.util.Date
import java.util.TimeZone
import kotlin.time.Duration
import kotlin.time.Duration.Companion.nanoseconds
object DateColumnAdapter : ColumnAdapter<Date, Long> {
override fun decode(databaseValue: Long): Date = Date(databaseValue)
override fun encode(value: Date): Long = value.time
}
private const val LIST_OF_STRINGS_SEPARATOR = ", "
object StringListColumnAdapter : ColumnAdapter<List<String>, String> {
override fun decode(databaseValue: String) = if (databaseValue.isEmpty()) {
emptyList()
} else {
databaseValue.split(LIST_OF_STRINGS_SEPARATOR)
}
override fun encode(value: List<String>) = value.joinToString(
separator = LIST_OF_STRINGS_SEPARATOR
)
}
object TimeZoneColumnAdapter : ColumnAdapter<TimeZone, String> {
override fun decode(databaseValue: String): TimeZone = TimeZone.getTimeZone(databaseValue)
override fun encode(value: TimeZone): String = value.id
}
object WeatherCodeColumnAdapter : ColumnAdapter<WeatherCode, String> {
override fun decode(databaseValue: String): WeatherCode =
WeatherCode.getInstance(databaseValue) ?: WeatherCode.CLEAR
override fun encode(value: WeatherCode): String = value.id
}
object AlertSeverityColumnAdapter : ColumnAdapter<AlertSeverity, Long> {
override fun decode(databaseValue: Long): AlertSeverity = AlertSeverity.getInstance(databaseValue.toInt())
override fun encode(value: AlertSeverity): Long = value.id.toLong()
}
object TemperatureColumnAdapter : ColumnAdapter<Temperature, Long> {
override fun decode(databaseValue: Long): Temperature = databaseValue.deciCelsius
override fun encode(value: Temperature): Long = value.value
}
object PrecipitationColumnAdapter : ColumnAdapter<Precipitation, Long> {
override fun decode(databaseValue: Long): Precipitation = databaseValue.micrometers
override fun encode(value: Precipitation): Long = value.value
}
object SpeedColumnAdapter : ColumnAdapter<Speed, Long> {
override fun decode(databaseValue: Long): Speed = databaseValue.centimetersPerSecond
override fun encode(value: Speed): Long = value.value
}
object DistanceColumnAdapter : ColumnAdapter<Distance, Long> {
override fun decode(databaseValue: Long): Distance = databaseValue.meters
override fun encode(value: Distance): Long = value.value
}
object PressureColumnAdapter : ColumnAdapter<Pressure, Long> {
override fun decode(databaseValue: Long): Pressure = databaseValue.pascals
override fun encode(value: Pressure): Long = value.value
}
object PollutantConcentrationColumnAdapter : ColumnAdapter<PollutantConcentration, Long> {
override fun decode(databaseValue: Long): PollutantConcentration = databaseValue.microgramsPerCubicMeter
override fun encode(value: PollutantConcentration): Long = value.value
}
object PollenConcentrationColumnAdapter : ColumnAdapter<PollenConcentration, Long> {
override fun decode(databaseValue: Long): PollenConcentration = databaseValue.perCubicMeter
override fun encode(value: PollenConcentration): Long = value.value
}
object DurationColumnAdapter : ColumnAdapter<Duration, Long> {
override fun decode(databaseValue: Long): Duration = databaseValue.nanoseconds
override fun encode(value: Duration): Long = value.inWholeNanoseconds
}
object RatioColumnAdapter : ColumnAdapter<Ratio, Long> {
override fun decode(databaseValue: Long): Ratio = databaseValue.permille
override fun encode(value: Ratio): Long = value.value
}

View file

@ -0,0 +1,58 @@
/**
* This file is part of Breezy Weather.
*
* Breezy Weather is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Breezy Weather is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Breezy Weather. If not, see <https://www.gnu.org/licenses/>.
*/
package breezyweather.data
import app.cash.sqldelight.ExecutableQuery
import app.cash.sqldelight.Query
import kotlinx.coroutines.flow.Flow
interface DatabaseHandler {
suspend fun <T> await(inTransaction: Boolean = false, block: suspend Database.() -> T): T
suspend fun <T : Any> awaitList(
inTransaction: Boolean = false,
block: suspend Database.() -> Query<T>,
): List<T>
suspend fun <T : Any> awaitOne(
inTransaction: Boolean = false,
block: suspend Database.() -> Query<T>,
): T
suspend fun <T : Any> awaitOneExecutable(
inTransaction: Boolean = false,
block: suspend Database.() -> ExecutableQuery<T>,
): T
suspend fun <T : Any> awaitOneOrNull(
inTransaction: Boolean = false,
block: suspend Database.() -> Query<T>,
): T?
suspend fun <T : Any> awaitOneOrNullExecutable(
inTransaction: Boolean = false,
block: suspend Database.() -> ExecutableQuery<T>,
): T?
fun <T : Any> subscribeToList(block: Database.() -> Query<T>): Flow<List<T>>
fun <T : Any> subscribeToOne(block: Database.() -> Query<T>): Flow<T>
fun <T : Any> subscribeToOneOrNull(block: Database.() -> Query<T>): Flow<T?>
}

View file

@ -0,0 +1,177 @@
/**
* This file is part of Breezy Weather.
*
* Breezy Weather is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Breezy Weather is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Breezy Weather. If not, see <https://www.gnu.org/licenses/>.
*/
package breezyweather.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Job
import kotlinx.coroutines.asContextElement
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.atomic.AtomicInteger
import kotlin.coroutines.ContinuationInterceptor
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.coroutineContext
import kotlin.coroutines.resume
/**
* Returns the transaction dispatcher if we are on a transaction, or the database dispatchers.
*/
internal suspend fun AndroidDatabaseHandler.getCurrentDatabaseContext(): CoroutineContext {
return coroutineContext[TransactionElement]?.transactionDispatcher ?: queryDispatcher
}
/**
* Calls the specified suspending [block] in a database transaction. The transaction will be
* marked as successful unless an exception is thrown in the suspending [block] or the coroutine
* is cancelled.
*
* SQLDelight will only perform at most one transaction at a time, additional transactions are queued
* and executed on a first come, first serve order.
*
* Performing blocking database operations is not permitted in a coroutine scope other than the
* one received by the suspending block. It is recommended that all [Dao] function invoked within
* the [block] be suspending functions.
*
* The dispatcher used to execute the given [block] will utilize threads from SQLDelight's query executor.
*/
internal suspend fun <T> AndroidDatabaseHandler.withTransaction(block: suspend () -> T): T {
// Use inherited transaction context if available, this allows nested suspending transactions.
val transactionContext = coroutineContext[TransactionElement]?.transactionDispatcher ?: createTransactionContext()
return withContext(transactionContext) {
val transactionElement = coroutineContext[TransactionElement]!!
transactionElement.acquire()
try {
db.transactionWithResult {
runBlocking(transactionContext) {
block()
}
}
} finally {
transactionElement.release()
}
}
}
/**
* Creates a [CoroutineContext] for performing database operations within a coroutine transaction.
*
* The context is a combination of a dispatcher, a [TransactionElement] and a thread local element.
*
* * The dispatcher will dispatch coroutines to a single thread that is taken over from the SQLDelight
* query executor. If the coroutine context is switched, suspending DAO functions will be able to
* dispatch to the transaction thread.
*
* * The [TransactionElement] serves as an indicator for inherited context, meaning, if there is a
* switch of context, suspending DAO methods will be able to use the indicator to dispatch the
* database operation to the transaction thread.
*
* * The thread local element serves as a second indicator and marks threads that are used to
* execute coroutines within the coroutine transaction, more specifically it allows us to identify
* if a blocking DAO method is invoked within the transaction coroutine. Never assign meaning to
* this value, for now all we care is if its present or not.
*/
private suspend fun AndroidDatabaseHandler.createTransactionContext(): CoroutineContext {
val controlJob = Job()
// make sure to tie the control job to this context to avoid blocking the transaction if
// context get cancelled before we can even start using this job. Otherwise, the acquired
// transaction thread will forever wait for the controlJob to be cancelled.
// see b/148181325
coroutineContext[Job]?.invokeOnCompletion {
controlJob.cancel()
}
val dispatcher = transactionDispatcher.acquireTransactionThread(controlJob)
val transactionElement = TransactionElement(controlJob, dispatcher)
val threadLocalElement =
suspendingTransactionId.asContextElement(System.identityHashCode(controlJob))
return dispatcher + transactionElement + threadLocalElement
}
/**
* Acquires a thread from the executor and returns a [ContinuationInterceptor] to dispatch
* coroutines to the acquired thread. The [controlJob] is used to control the release of the
* thread by cancelling the job.
*/
private suspend fun CoroutineDispatcher.acquireTransactionThread(
controlJob: Job,
): ContinuationInterceptor {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
// We got cancelled while waiting to acquire a thread, we can't stop our attempt to
// acquire a thread, but we can cancel the controlling job so once it gets acquired it
// is quickly released.
controlJob.cancel()
}
try {
dispatch(EmptyCoroutineContext) {
runBlocking {
// Thread acquired, resume coroutine
continuation.resume(coroutineContext[ContinuationInterceptor]!!)
controlJob.join()
}
}
} catch (ex: RejectedExecutionException) {
// Couldn't acquire a thread, cancel coroutine
continuation.cancel(
IllegalStateException(
"Unable to acquire a thread to perform the database transaction",
ex
)
)
}
}
}
/**
* A [CoroutineContext.Element] that indicates there is an on-going database transaction.
*/
private class TransactionElement(
private val transactionThreadControlJob: Job,
val transactionDispatcher: ContinuationInterceptor,
) : CoroutineContext.Element {
companion object Key : CoroutineContext.Key<TransactionElement>
override val key: CoroutineContext.Key<TransactionElement>
get() = TransactionElement
/**
* Number of transactions (including nested ones) started with this element.
* Call [acquire] to increase the count and [release] to decrease it. If the count reaches zero
* when [release] is invoked then the transaction job is cancelled and the transaction thread
* is released.
*/
private val referenceCount = AtomicInteger(0)
fun acquire() {
referenceCount.incrementAndGet()
}
fun release() {
val count = referenceCount.decrementAndGet()
if (count < 0) {
throw IllegalStateException("Transaction was never started or was already released")
} else if (count == 0) {
// Cancel the job that controls the transaction thread, causing it to be released.
transactionThreadControlJob.cancel()
}
}
}

View file

@ -0,0 +1,83 @@
/*
* This file is part of Breezy Weather.
*
* Breezy Weather is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, version 3 of the License.
*
* Breezy Weather is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Breezy Weather. If not, see <https://www.gnu.org/licenses/>.
*/
package breezyweather.data.location
import breezyweather.domain.location.model.Location
import java.util.TimeZone
object LocationMapper {
fun mapLocation(
cityId: String?,
latitude: Double,
longitude: Double,
timeZone: TimeZone,
customName: String?,
country: String,
countryCode: String?,
admin1: String?,
admin1Code: String?,
admin2: String?,
admin2Code: String?,
admin3: String?,
admin3Code: String?,
admin4: String?,
admin4Code: String?,
city: String,
district: String?,
weatherSource: String,
currentSource: String?,
airQualitySource: String?,
pollenSource: String?,
minutelySource: String?,
alertSource: String?,
normalsSource: String?,
reverseGeocodingSource: String?,
isCurrentPosition: Boolean,
needsGeocodeRefresh: Boolean,
backgroundWeatherKind: String?, // TODO: Deprecated
backgroundDayNightType: String?, // TODO: Deprecated
): Location = Location(
cityId = cityId,
latitude = latitude,
longitude = longitude,
timeZone = timeZone,
customName = customName,
country = country,
countryCode = countryCode,
admin1 = admin1,
admin1Code = admin1Code,
admin2 = admin2,
admin2Code = admin2Code,
admin3 = admin3,
admin3Code = admin3Code,
admin4 = admin4,
admin4Code = admin4Code,
city = city,
district = district,
forecastSource = weatherSource,
currentSource = currentSource,
airQualitySource = airQualitySource,
pollenSource = pollenSource,
minutelySource = minutelySource,
alertSource = alertSource,
normalsSource = normalsSource,
reverseGeocodingSource = reverseGeocodingSource,
isCurrentPosition = isCurrentPosition,
needsGeocodeRefresh = needsGeocodeRefresh
)
}

View file

@ -0,0 +1,295 @@
/**
* This file is part of Breezy Weather.
*
* Breezy Weather is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Breezy Weather is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Breezy Weather. If not, see <https://www.gnu.org/licenses/>.
*/
package breezyweather.data.location
import android.util.Log
import breezyweather.data.DatabaseHandler
import breezyweather.domain.location.model.Location
class LocationRepository(
private val handler: DatabaseHandler,
) {
suspend fun getLocation(formattedId: String, withParameters: Boolean = true): Location? {
val location = handler.awaitOneOrNull {
locationsQueries.getLocationById(formattedId, LocationMapper::mapLocation)
}
return if (withParameters) {
location?.copy(
// I'm sure there must be a more efficient way
parameters = handler.awaitList {
location_parametersQueries.getLocationParametersByLocationId(location.formattedId)
}.groupBy(
{ it.source },
{ it.parameter to it.value_ }
).mapValues { parameterList ->
parameterList.value.associate { it.first to it.second }
}
)
} else {
location
}
}
suspend fun getFirstLocation(withParameters: Boolean = true): Location? {
val location = handler.awaitOneOrNull {
locationsQueries.getFirstLocation(LocationMapper::mapLocation)
}
return if (withParameters) {
location?.copy(
// I'm sure there must be a more efficient way
parameters = handler.awaitList {
location_parametersQueries.getLocationParametersByLocationId(location.formattedId)
}.groupBy(
{ it.source },
{ it.parameter to it.value_ }
).mapValues { parameterList ->
parameterList.value.associate { it.first to it.second }
}
)
} else {
location
}
}
suspend fun getXLocations(limit: Int, withParameters: Boolean = true): List<Location> {
val locations = handler.awaitList {
locationsQueries.getXLocations(limit.toLong(), LocationMapper::mapLocation)
}
return if (withParameters) {
locations.map { location ->
location.copy(
// I'm sure there must be a more efficient way
parameters = handler.awaitList {
location_parametersQueries.getLocationParametersByLocationId(location.formattedId)
}.groupBy(
{ it.source },
{ it.parameter to it.value_ }
).mapValues { parameterList ->
parameterList.value.associate { it.first to it.second }
}
)
}
} else {
locations
}
}
suspend fun getAllLocations(withParameters: Boolean = true): List<Location> {
val locations = handler.awaitList {
locationsQueries.getAllLocations(LocationMapper::mapLocation)
}
return if (withParameters) {
locations.map { location ->
location.copy(
// I'm sure there must be a more efficient way
parameters = handler.awaitList {
location_parametersQueries.getLocationParametersByLocationId(location.formattedId)
}.groupBy(
{ it.source },
{ it.parameter to it.value_ }
).mapValues { parameterList ->
parameterList.value.associate { it.first to it.second }
}
)
}
} else {
locations
}
}
suspend fun addAll(locations: List<Location>): List<Location> {
return try {
handler.await(inTransaction = true) {
// 1. Delete every location that was removed
locationsQueries.deleteAllNonMatchingLocations(locations.map { it.formattedId })
// 2. Insert/Replace locations
locations.mapIndexed { index, location ->
locationsQueries.insert( // Will do a replace if formattedId already exists
formattedId = location.formattedId,
listOrder = index + 1L,
cityId = location.cityId,
latitude = location.latitude,
longitude = location.longitude,
timezone = location.timeZone,
customName = location.customName,
country = location.country,
countryCode = location.countryCode,
admin1 = location.admin1,
admin1Code = location.admin1Code,
admin2 = location.admin2,
admin2Code = location.admin2Code,
admin3 = location.admin3,
admin3Code = location.admin3Code,
admin4 = location.admin4,
admin4Code = location.admin4Code,
city = location.city,
district = location.district,
weatherSource = location.forecastSource,
currentSource = location.currentSource,
airQualitySource = location.airQualitySource,
pollenSource = location.pollenSource,
minutelySource = location.minutelySource,
alertSource = location.alertSource,
normalsSource = location.normalsSource,
reverseGeocodingSource = location.reverseGeocodingSource,
currentPosition = location.isCurrentPosition,
needsGeocodeRefresh = location.needsGeocodeRefresh,
backgroundWeatherKind = null, // TODO: Deprecated
backgroundDayNightType = null // TODO: Deprecated
)
// 3. Update location parameters
// 3a. Delete no longer existing parameters
location_parametersQueries.deleteAllNonMatchingParameters(
location.formattedId,
location.parameters.map { it.key }
)
// 3b. Insert/replace parameters
location.parameters.forEach { source ->
source.value.forEach {
// Will do a replace if formattedId/parameter already exists
location_parametersQueries.insert(
location.formattedId,
source.key,
it.key,
it.value
)
}
}
location
}
}
} catch (e: Exception) {
Log.e("BreezyWeather", e.toString())
emptyList()
}
}
suspend fun insertParameters(locationFormattedId: String, locationParameters: Map<String, Map<String, String>>) {
handler.await(inTransaction = true) {
// 3a. Delete no longer existing parameters
location_parametersQueries.deleteAllNonMatchingParameters(
locationFormattedId,
locationParameters.map { it.key }
)
// 3b. Insert/replace parameters
locationParameters.forEach { source ->
source.value.forEach {
// Will do a replace if formattedId/parameter already exists
location_parametersQueries.insert(
locationFormattedId,
source.key,
it.key,
it.value
)
}
}
}
}
suspend fun updateParameters(
source: String,
parameter: String,
values: Map<String, String>,
) {
handler.await {
values.entries.forEach { (oldValue, newValue) ->
location_parametersQueries.updateParameters(
source = source,
parameter = parameter,
oldValue = oldValue,
newValue = newValue
)
}
}
}
suspend fun deleteParameters(
source: String,
parameter: String,
values: List<String>,
) {
handler.await {
location_parametersQueries.deleteParameters(
source,
parameter,
values
)
}
}
suspend fun update(location: Location, oldFormattedId: String? = null): Boolean {
return try {
handler.await(inTransaction = true) {
if (oldFormattedId != null && oldFormattedId != location.formattedId) {
locationsQueries.updateFormattedId(
oldFormattedId = oldFormattedId,
newFormattedId = location.formattedId
)
}
locationsQueries.update(
formattedId = location.formattedId,
cityId = location.cityId,
latitude = location.latitude,
longitude = location.longitude,
timezone = location.timeZone.id,
customName = location.customName,
country = location.country,
countryCode = location.countryCode,
admin1 = location.admin1,
admin1Code = location.admin1Code,
admin2 = location.admin2,
admin2Code = location.admin2Code,
admin3 = location.admin3,
admin3Code = location.admin3Code,
admin4 = location.admin4,
admin4Code = location.admin4Code,
city = location.city,
district = location.district,
weatherSource = location.forecastSource,
currentSource = location.currentSource,
airQualitySource = location.airQualitySource,
pollenSource = location.pollenSource,
minutelySource = location.minutelySource,
alertSource = location.alertSource,
normalsSource = location.normalsSource,
reverseGeocodingSource = location.reverseGeocodingSource,
currentPosition = location.isCurrentPosition,
needsGeocodeRefresh = location.needsGeocodeRefresh,
backgroundWeatherKind = null, // TODO: Deprecated
backgroundDayNightType = null // TODO: Deprecated
)
}
true
} catch (e: Exception) {
Log.e("BreezyWeather", e.toString())
false
}
}
suspend fun delete(formattedId: String) {
handler.await {
locationsQueries.deleteLocation(formattedId)
}
}
}

View file

@ -0,0 +1,516 @@
/*
* This file is part of Breezy Weather.
*
* Breezy Weather is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, version 3 of the License.
*
* Breezy Weather is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Breezy Weather. If not, see <https://www.gnu.org/licenses/>.
*/
package breezyweather.data.weather
import breezyweather.domain.weather.model.AirQuality
import breezyweather.domain.weather.model.Alert
import breezyweather.domain.weather.model.Astro
import breezyweather.domain.weather.model.Base
import breezyweather.domain.weather.model.Current
import breezyweather.domain.weather.model.Daily
import breezyweather.domain.weather.model.DailyCloudCover
import breezyweather.domain.weather.model.DailyDewPoint
import breezyweather.domain.weather.model.DailyPressure
import breezyweather.domain.weather.model.DailyRelativeHumidity
import breezyweather.domain.weather.model.DailyVisibility
import breezyweather.domain.weather.model.DegreeDay
import breezyweather.domain.weather.model.HalfDay
import breezyweather.domain.weather.model.Hourly
import breezyweather.domain.weather.model.Minutely
import breezyweather.domain.weather.model.MoonPhase
import breezyweather.domain.weather.model.Normals
import breezyweather.domain.weather.model.Pollen
import breezyweather.domain.weather.model.PrecipitationDuration
import breezyweather.domain.weather.model.PrecipitationProbability
import breezyweather.domain.weather.model.UV
import breezyweather.domain.weather.model.Weather
import breezyweather.domain.weather.model.Wind
import breezyweather.domain.weather.reference.AlertSeverity
import breezyweather.domain.weather.reference.Month
import breezyweather.domain.weather.reference.WeatherCode
import org.breezyweather.unit.distance.Distance
import org.breezyweather.unit.pollen.PollenConcentration
import org.breezyweather.unit.pollutant.PollutantConcentration
import org.breezyweather.unit.precipitation.Precipitation
import org.breezyweather.unit.pressure.Pressure
import org.breezyweather.unit.ratio.Ratio
import org.breezyweather.unit.speed.Speed
import org.breezyweather.unit.temperature.Temperature
import java.util.Date
import kotlin.time.Duration
object WeatherMapper {
fun mapWeather(
refreshTime: Long?,
mainUpdateTime: Long?,
currentUpdateTime: Long?,
airQualityUpdateTime: Long?,
pollenUpdateTime: Long?,
minutelyUpdateTime: Long?,
alertsUpdateTime: Long?,
normalsUpdateTime: Long?,
normalsUpdateLatitude: Double,
normalsUpdateLongitude: Double,
weatherText: String?,
weatherCode: WeatherCode?,
temperature: Temperature?,
sourceFeelsLikeTemperature: Temperature?,
apparentTemperature: Temperature?,
windChillTemperature: Temperature?,
humidex: Temperature?,
windDegree: Double?,
windSpeed: Speed?,
windGusts: Speed?,
uvIndex: Double?,
pm25: PollutantConcentration?,
pm10: PollutantConcentration?,
so2: PollutantConcentration?,
no2: PollutantConcentration?,
o3: PollutantConcentration?,
co: PollutantConcentration?,
relativeHumidity: Ratio?,
dewPoint: Temperature?,
pressure: Pressure?,
visibility: Distance?,
cloudCover: Ratio?,
ceiling: Distance?,
dailyForecast: String?,
hourlyForecast: String?,
): Weather = Weather(
Base(
refreshTime?.let { Date(it) },
mainUpdateTime?.let { Date(it) },
currentUpdateTime?.let { Date(it) },
airQualityUpdateTime?.let { Date(it) },
pollenUpdateTime?.let { Date(it) },
minutelyUpdateTime?.let { Date(it) },
alertsUpdateTime?.let { Date(it) },
normalsUpdateTime?.let { Date(it) },
normalsUpdateLatitude,
normalsUpdateLongitude
),
Current(
weatherText,
weatherCode,
breezyweather.domain.weather.model.Temperature(
temperature,
sourceFeelsLike = sourceFeelsLikeTemperature,
computedApparent = apparentTemperature,
computedWindChill = windChillTemperature,
computedHumidex = humidex
),
Wind(
windDegree,
windSpeed,
windGusts
),
UV(uvIndex),
AirQuality(
pm25,
pm10,
so2,
no2,
o3,
co
),
relativeHumidity,
dewPoint,
pressure,
cloudCover,
visibility,
ceiling,
dailyForecast,
hourlyForecast
)
)
fun mapDaily(
date: Long,
daytimeWeatherText: String?,
daytimeweatherSummary: String?,
daytimeWeatherCode: WeatherCode?,
daytimeTemperature: Temperature?,
daytimeSourceFeelsLikeTemperature: Temperature?,
daytimeApparentTemperature: Temperature?,
daytimeWindChillTemperature: Temperature?,
daytimeHumidex: Temperature?,
daytimeTotalPrecipitation: Precipitation?,
daytimeThunderstormPrecipitation: Precipitation?,
daytimeRainPrecipitation: Precipitation?,
daytimeSnowPrecipitation: Precipitation?,
daytimeIcePrecipitation: Precipitation?,
daytimeTotalPrecipitationProbability: Ratio?,
daytimeThunderstormPrecipitationProbability: Ratio?,
daytimeRainPrecipitationProbability: Ratio?,
daytimeSnowPrecipitationProbability: Ratio?,
daytimeIcePrecipitationProbability: Ratio?,
daytimeTotalPrecipitationDuration: Duration?,
daytimeThunderstormPrecipitationDuration: Duration?,
daytimeRainPrecipitationDuration: Duration?,
daytimeSnowPrecipitationDuration: Duration?,
daytimeIcePrecipitationDuration: Duration?,
daytimeWindDegree: Double?,
daytimeWindSpeed: Speed?,
daytimeWindGusts: Speed?,
nighttimeWeatherText: String?,
nighttimeweatherSummary: String?,
nighttimeWeatherCode: WeatherCode?,
nighttimeTemperature: Temperature?,
nighttimeSourceFeelsLikeTemperature: Temperature?,
nighttimeApparentTemperature: Temperature?,
nighttimeWindChillTemperature: Temperature?,
nighttimeHumidex: Temperature?,
nighttimeTotalPrecipitation: Precipitation?,
nighttimeThunderstormPrecipitation: Precipitation?,
nighttimeRainPrecipitation: Precipitation?,
nighttimeSnowPrecipitation: Precipitation?,
nighttimeIcePrecipitation: Precipitation?,
nighttimeTotalPrecipitationProbability: Ratio?,
nighttimeThunderstormPrecipitationProbability: Ratio?,
nighttimeRainPrecipitationProbability: Ratio?,
nighttimeSnowPrecipitationProbability: Ratio?,
nighttimeIcePrecipitationProbability: Ratio?,
nighttimeTotalPrecipitationDuration: Duration?,
nighttimeThunderstormPrecipitationDuration: Duration?,
nighttimeRainPrecipitationDuration: Duration?,
nighttimeSnowPrecipitationDuration: Duration?,
nighttimeIcePrecipitationDuration: Duration?,
nighttimeWindDegree: Double?,
nighttimeWindSpeed: Speed?,
nighttimeWindGusts: Speed?,
degreeDayHeating: Temperature?,
degreeDayCooling: Temperature?,
sunRiseDate: Long?,
sunSetDate: Long?,
twilightRiseDate: Long?,
twilightSetDate: Long?,
moonRiseDate: Long?,
moonSetDate: Long?,
moonPhaseAngle: Long?,
pm25: PollutantConcentration?,
pm10: PollutantConcentration?,
so2: PollutantConcentration?,
no2: PollutantConcentration?,
o3: PollutantConcentration?,
co: PollutantConcentration?,
alder: PollenConcentration?,
ash: PollenConcentration?,
birch: PollenConcentration?,
chestnut: PollenConcentration?,
cypress: PollenConcentration?,
grass: PollenConcentration?,
hazel: PollenConcentration?,
hornbeam: PollenConcentration?,
linden: PollenConcentration?,
mold: PollenConcentration?,
mugwort: PollenConcentration?,
oak: PollenConcentration?,
olive: PollenConcentration?,
plane: PollenConcentration?,
plantain: PollenConcentration?,
poplar: PollenConcentration?,
ragweed: PollenConcentration?,
sorrel: PollenConcentration?,
tree: PollenConcentration?,
urticaceae: PollenConcentration?,
willow: PollenConcentration?,
uvIndex: Double?,
sunshineDuration: Duration?,
relativeHumidityAverage: Ratio?,
relativeHumidityMin: Ratio?,
relativeHumidityMax: Ratio?,
dewpointAverage: Temperature?,
dewpointMin: Temperature?,
dewpointMax: Temperature?,
pressureAverage: Pressure?,
pressureMin: Pressure?,
pressureMax: Pressure?,
cloudCoverAverage: Ratio?,
cloudCoverMin: Ratio?,
cloudCoverMax: Ratio?,
visibilityAverage: Distance?,
visibilityMin: Distance?,
visibilityMax: Distance?,
): Daily = Daily(
Date(date),
HalfDay(
daytimeWeatherText,
daytimeweatherSummary,
daytimeWeatherCode,
breezyweather.domain.weather.model.Temperature(
daytimeTemperature,
sourceFeelsLike = daytimeSourceFeelsLikeTemperature,
daytimeApparentTemperature,
daytimeWindChillTemperature,
daytimeHumidex
),
breezyweather.domain.weather.model.Precipitation(
daytimeTotalPrecipitation,
daytimeThunderstormPrecipitation,
daytimeRainPrecipitation,
daytimeSnowPrecipitation,
daytimeIcePrecipitation
),
PrecipitationProbability(
daytimeTotalPrecipitationProbability,
daytimeThunderstormPrecipitationProbability,
daytimeRainPrecipitationProbability,
daytimeSnowPrecipitationProbability,
daytimeIcePrecipitationProbability
),
PrecipitationDuration(
daytimeTotalPrecipitationDuration,
daytimeThunderstormPrecipitationDuration,
daytimeRainPrecipitationDuration,
daytimeSnowPrecipitationDuration,
daytimeIcePrecipitationDuration
),
Wind(
daytimeWindDegree,
daytimeWindSpeed,
daytimeWindGusts
)
),
HalfDay(
nighttimeWeatherText,
nighttimeweatherSummary,
nighttimeWeatherCode,
breezyweather.domain.weather.model.Temperature(
nighttimeTemperature,
sourceFeelsLike = nighttimeSourceFeelsLikeTemperature,
nighttimeApparentTemperature,
nighttimeWindChillTemperature,
nighttimeHumidex
),
breezyweather.domain.weather.model.Precipitation(
nighttimeTotalPrecipitation,
nighttimeThunderstormPrecipitation,
nighttimeRainPrecipitation,
nighttimeSnowPrecipitation,
nighttimeIcePrecipitation
),
PrecipitationProbability(
nighttimeTotalPrecipitationProbability,
nighttimeThunderstormPrecipitationProbability,
nighttimeRainPrecipitationProbability,
nighttimeSnowPrecipitationProbability,
nighttimeIcePrecipitationProbability
),
PrecipitationDuration(
nighttimeTotalPrecipitationDuration,
nighttimeThunderstormPrecipitationDuration,
nighttimeRainPrecipitationDuration,
nighttimeSnowPrecipitationDuration,
nighttimeIcePrecipitationDuration
),
Wind(
nighttimeWindDegree,
nighttimeWindSpeed,
nighttimeWindGusts
)
),
DegreeDay(degreeDayHeating, degreeDayCooling),
Astro(sunRiseDate?.let { Date(it) }, sunSetDate?.let { Date(it) }),
Astro(twilightRiseDate?.let { Date(it) }, twilightSetDate?.let { Date(it) }),
Astro(moonRiseDate?.let { Date(it) }, moonSetDate?.let { Date(it) }),
MoonPhase(moonPhaseAngle?.toInt()),
AirQuality(
pm25,
pm10,
so2,
no2,
o3,
co
),
Pollen(
alder = alder,
ash = ash,
birch = birch,
chestnut = chestnut,
cypress = cypress,
grass = grass,
hazel = hazel,
hornbeam = hornbeam,
linden = linden,
mugwort = mugwort,
mold = mold,
oak = oak,
olive = olive,
plane = plane,
plantain = plantain,
poplar = poplar,
ragweed = ragweed,
sorrel = sorrel,
tree = tree,
urticaceae = urticaceae,
willow = willow
),
UV(uvIndex),
sunshineDuration,
relativeHumidity = DailyRelativeHumidity(
average = relativeHumidityAverage,
min = relativeHumidityMin,
max = relativeHumidityMax
),
dewPoint = DailyDewPoint(
average = dewpointAverage,
min = dewpointMin,
max = dewpointMax
),
pressure = DailyPressure(
average = pressureAverage,
min = pressureMin,
max = pressureMax
),
cloudCover = DailyCloudCover(
average = cloudCoverAverage,
min = cloudCoverMin,
max = cloudCoverMax
),
visibility = DailyVisibility(
average = visibilityAverage,
min = visibilityMin,
max = visibilityMax
)
)
fun mapHourly(
date: Long,
daylight: Boolean,
weatherText: String?,
weatherCode: WeatherCode?,
temperature: Temperature?,
sourceFeelsLikeTemperature: Temperature?,
apparentTemperature: Temperature?,
windChillTemperature: Temperature?,
humidex: Temperature?,
totalPrecipitation: Precipitation?,
thunderstormPrecipitation: Precipitation?,
rainPrecipitation: Precipitation?,
snowPrecipitation: Precipitation?,
icePrecipitation: Precipitation?,
totalPrecipitationProbability: Ratio?,
thunderstormPrecipitationProbability: Ratio?,
rainPrecipitationProbability: Ratio?,
snowPrecipitationProbability: Ratio?,
icePrecipitationProbability: Ratio?,
windDegree: Double?,
windSpeed: Speed?,
windGusts: Speed?,
pm25: PollutantConcentration?,
pm10: PollutantConcentration?,
so2: PollutantConcentration?,
no2: PollutantConcentration?,
o3: PollutantConcentration?,
co: PollutantConcentration?,
uvIndex: Double?,
relativeHumidity: Ratio?,
dewPoint: Temperature?,
pressure: Pressure?,
cloudCover: Ratio?,
visibility: Distance?,
): Hourly = Hourly(
Date(date),
daylight,
weatherText,
weatherCode,
breezyweather.domain.weather.model.Temperature(
temperature,
sourceFeelsLike = sourceFeelsLikeTemperature,
computedApparent = apparentTemperature,
computedWindChill = windChillTemperature,
computedHumidex = humidex
),
breezyweather.domain.weather.model.Precipitation(
totalPrecipitation,
thunderstormPrecipitation,
rainPrecipitation,
snowPrecipitation,
icePrecipitation
),
PrecipitationProbability(
totalPrecipitationProbability,
thunderstormPrecipitationProbability,
rainPrecipitationProbability,
snowPrecipitationProbability,
icePrecipitationProbability
),
Wind(
windDegree,
windSpeed,
windGusts
),
AirQuality(
pm25,
pm10,
so2,
no2,
o3,
co
),
UV(uvIndex),
relativeHumidity,
dewPoint,
pressure,
cloudCover,
visibility
)
fun mapAlert(
alertId: String,
startDate: Long?,
endDate: Long?,
headline: String?,
description: String?,
instruction: String?,
source: String?,
severity: AlertSeverity,
color: Long,
): Alert = Alert(
alertId = alertId,
startDate = startDate?.let { Date(it) },
endDate = endDate?.let { Date(it) },
headline = headline,
description = description,
instruction = instruction,
source = source,
severity = severity,
color = color.toInt()
)
fun mapMinutely(
date: Long,
minuteInterval: Long,
intensity: Precipitation?,
): Minutely = Minutely(
Date(date),
minuteInterval.toInt(),
intensity
)
fun mapNormals(
month: Long,
daytimeTemperature: Temperature?,
nighttimeTemperature: Temperature?,
): Map<Month, Normals> = mapOf(
Month.of(month.toInt()) to Normals(
daytimeTemperature,
nighttimeTemperature
)
)
}

View file

@ -0,0 +1,422 @@
/**
* This file is part of Breezy Weather.
*
* Breezy Weather is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Breezy Weather is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Breezy Weather. If not, see <https://www.gnu.org/licenses/>.
*/
package breezyweather.data.weather
import breezyweather.data.DatabaseHandler
import breezyweather.domain.location.model.Location
import breezyweather.domain.weather.model.Alert
import breezyweather.domain.weather.model.Daily
import breezyweather.domain.weather.model.Hourly
import breezyweather.domain.weather.model.Minutely
import breezyweather.domain.weather.model.Normals
import breezyweather.domain.weather.model.Weather
import breezyweather.domain.weather.reference.Month
import java.util.Date
class WeatherRepository(
private val handler: DatabaseHandler,
) {
suspend fun getWeatherByLocationId(
locationFormattedId: String,
withDaily: Boolean = true,
withHourly: Boolean = true,
withMinutely: Boolean = true,
withAlerts: Boolean = true,
withNormals: Boolean = true,
): Weather? {
val weather = handler.awaitOneOrNull {
weathersQueries.getWeatherByLocationId(locationFormattedId, WeatherMapper::mapWeather)
}
return if (withDaily || withHourly || withMinutely || withAlerts) {
weather?.copy(
dailyForecast = if (withDaily) {
getDailyListByLocationId(locationFormattedId)
} else {
emptyList()
},
hourlyForecast = if (withHourly) {
getHourlyListByLocationId(locationFormattedId)
} else {
emptyList()
},
minutelyForecast = if (withMinutely) {
getMinutelyListByLocationId(locationFormattedId)
} else {
emptyList()
},
alertList = if (withAlerts) {
getAlertListByLocationId(locationFormattedId)
} else {
emptyList()
},
normals = if (withNormals) {
getNormalsByLocationId(locationFormattedId)
} else {
emptyMap()
}
)
} else {
weather
}
}
suspend fun getDailyListByLocationId(locationFormattedId: String): List<Daily> {
return handler.awaitList {
dailysQueries.getDailyListByLocationId(locationFormattedId, WeatherMapper::mapDaily)
}
}
suspend fun getHourlyListByLocationId(locationFormattedId: String): List<Hourly> {
return handler.awaitList {
hourlysQueries.getHourlyListByLocationId(locationFormattedId, WeatherMapper::mapHourly)
}
}
suspend fun getMinutelyListByLocationId(locationFormattedId: String): List<Minutely> {
return handler.awaitList {
minutelysQueries.getMinutelyListByLocationId(locationFormattedId, WeatherMapper::mapMinutely)
}
}
suspend fun getAlertListByLocationId(locationFormattedId: String): List<Alert> {
return handler.awaitList {
alertsQueries.getAlertListByLocationId(locationFormattedId, WeatherMapper::mapAlert)
}
}
suspend fun getNormalsByLocationId(locationFormattedId: String): Map<Month, Normals> {
return handler.awaitList {
normalsQueries.getNormalsByLocationId(locationFormattedId)
}.associate {
Month.of(it.month.toInt()) to Normals(
daytimeTemperature = it.temperature_max_average,
nighttimeTemperature = it.temperature_min_average
)
}
}
suspend fun getCurrentAlertsByLocationId(locationFormattedId: String): List<Alert> {
return handler.awaitList {
alertsQueries.getCurrentAlertsByLocationId(
locationFormattedId,
Date().time,
WeatherMapper::mapAlert
)
}
}
suspend fun insert(location: Location, weather: Weather) {
handler.await(inTransaction = true) {
// 1. Save Weather (will replace if necessary)
weathersQueries.insert(
// base
locationFormattedId = location.formattedId,
refreshTime = weather.base.refreshTime?.time,
mainUpdateTime = weather.base.forecastUpdateTime?.time,
currentUpdateTime = weather.base.currentUpdateTime?.time,
airQualityUpdateTime = weather.base.airQualityUpdateTime?.time,
pollenUpdateTime = weather.base.pollenUpdateTime?.time,
minutelyUpdateTime = weather.base.minutelyUpdateTime?.time,
alertsUpdateTime = weather.base.alertsUpdateTime?.time,
normalsUpdateTime = weather.base.normalsUpdateTime?.time,
normalsUpdateLatitude = weather.base.normalsUpdateLatitude,
normalsUpdateLongitude = weather.base.normalsUpdateLongitude,
// current
weatherText = weather.current?.weatherText,
weatherCode = weather.current?.weatherCode,
temperature = weather.current?.temperature?.temperature,
temperatureSourceFeelsLike = weather.current?.temperature?.sourceFeelsLike,
temperatureApparent = weather.current?.temperature?.computedApparent,
temperatureWindChill = weather.current?.temperature?.computedWindChill,
humidex = weather.current?.temperature?.computedHumidex,
windDegree = weather.current?.wind?.degree,
windSpeed = weather.current?.wind?.speed,
windGusts = weather.current?.wind?.gusts,
uvIndex = weather.current?.uV?.index,
pm25 = weather.current?.airQuality?.pM25,
pm10 = weather.current?.airQuality?.pM10,
so2 = weather.current?.airQuality?.sO2,
no2 = weather.current?.airQuality?.nO2,
o3 = weather.current?.airQuality?.o3,
co = weather.current?.airQuality?.cO,
relativeHumidity = weather.current?.relativeHumidity,
dewPoint = weather.current?.dewPoint,
pressure = weather.current?.pressure,
cloudCover = weather.current?.cloudCover,
visibility = weather.current?.visibility,
ceiling = weather.current?.ceiling,
dailyForecast = weather.current?.dailyForecast,
hourlyForecast = weather.current?.hourlyForecast
)
// 2. Save daily (delete first, then re-add)
dailysQueries.deleteDailyListForLocationId(location.formattedId)
weather.dailyForecast.forEach { daily ->
dailysQueries.insert(
locationFormattedId = location.formattedId,
date = daily.date.time,
// daytime.
daytimeWeatherText = daily.day?.weatherText,
daytimeweatherSummary = daily.day?.weatherSummary,
daytimeWeatherCode = daily.day?.weatherCode,
daytimeTemperature = daily.day?.temperature?.temperature,
daytimeTemperatureSourceFeelsLike = daily.day?.temperature?.sourceFeelsLike,
daytimeTemperatureApparent = daily.day?.temperature?.computedApparent,
daytimeTemperatureWindChill = daily.day?.temperature?.computedWindChill,
daytimeHumidex = daily.day?.temperature?.computedHumidex,
daytimeTotalPrecipitation = daily.day?.precipitation?.total,
daytimeThunderstormPrecipitation = daily.day?.precipitation?.thunderstorm,
daytimeRainPrecipitation = daily.day?.precipitation?.rain,
daytimeSnowPrecipitation = daily.day?.precipitation?.snow,
daytimeIcePrecipitation = daily.day?.precipitation?.ice,
daytimeTotalPrecipitationProbability = daily.day?.precipitationProbability?.total,
daytimeThunderstormPrecipitationProbability = daily.day?.precipitationProbability?.thunderstorm,
daytimeRainPrecipitationProbability = daily.day?.precipitationProbability?.rain,
daytimeSnowPrecipitationProbability = daily.day?.precipitationProbability?.snow,
daytimeIcePrecipitationProbability = daily.day?.precipitationProbability?.ice,
daytimeTotalPrecipitationDuration = daily.day?.precipitationDuration?.total,
daytimeThunderstormPrecipitationDuration = daily.day?.precipitationDuration?.thunderstorm,
daytimeRainPrecipitationDuration = daily.day?.precipitationDuration?.rain,
daytimeSnowPrecipitationDuration = daily.day?.precipitationDuration?.snow,
daytimeIcePrecipitationDuration = daily.day?.precipitationDuration?.ice,
daytimeWindDegree = daily.day?.wind?.degree,
daytimeWindSpeed = daily.day?.wind?.speed,
daytimeWindGusts = daily.day?.wind?.gusts,
// nighttime.
nighttimeWeatherText = daily.night?.weatherText,
nighttimeweatherSummary = daily.night?.weatherSummary,
nighttimeWeatherCode = daily.night?.weatherCode,
nighttimeTemperature = daily.night?.temperature?.temperature,
nighttimeTemperatureSourceFeelsLike = daily.night?.temperature?.sourceFeelsLike,
nighttimeTemperatureApparent = daily.night?.temperature?.computedApparent,
nighttimeTemperatureWindChill = daily.night?.temperature?.computedWindChill,
nighttimeHumidex = daily.night?.temperature?.computedHumidex,
nighttimeTotalPrecipitation = daily.night?.precipitation?.total,
nighttimeThunderstormPrecipitation = daily.night?.precipitation?.thunderstorm,
nighttimeRainPrecipitation = daily.night?.precipitation?.rain,
nighttimeSnowPrecipitation = daily.night?.precipitation?.snow,
nighttimeIcePrecipitation = daily.night?.precipitation?.ice,
nighttimeTotalPrecipitationProbability = daily.night?.precipitationProbability?.total,
nighttimeThunderstormPrecipitationProbability = daily.night?.precipitationProbability?.thunderstorm,
nighttimeRainPrecipitationProbability = daily.night?.precipitationProbability?.rain,
nighttimeSnowPrecipitationProbability = daily.night?.precipitationProbability?.snow,
nighttimeIcePrecipitationProbability = daily.night?.precipitationProbability?.ice,
nighttimeTotalPrecipitationDuration = daily.night?.precipitationDuration?.total,
nighttimeThunderstormPrecipitationDuration = daily.night?.precipitationDuration?.thunderstorm,
nighttimeRainPrecipitationDuration = daily.night?.precipitationDuration?.rain,
nighttimeSnowPrecipitationDuration = daily.night?.precipitationDuration?.snow,
nighttimeIcePrecipitationDuration = daily.night?.precipitationDuration?.ice,
nighttimeWindDegree = daily.night?.wind?.degree,
nighttimeWindSpeed = daily.night?.wind?.speed,
nighttimeWindGusts = daily.night?.wind?.gusts,
degreeDayHeating = daily.degreeDay?.heating,
degreeDayCooling = daily.degreeDay?.cooling,
// sun
sunRiseDate = daily.sun?.riseDate?.time,
sunSetDate = daily.sun?.setDate?.time,
// twilight
twilightRiseDate = daily.twilight?.riseDate?.time,
twilightSetDate = daily.twilight?.setDate?.time,
// moon
moonRiseDate = daily.moon?.riseDate?.time,
moonSetDate = daily.moon?.setDate?.time,
// moon phase
moonPhaseAngle = daily.moonPhase?.angle?.toLong(),
// aqi.
pm25 = daily.airQuality?.pM25,
pm10 = daily.airQuality?.pM10,
so2 = daily.airQuality?.sO2,
no2 = daily.airQuality?.nO2,
o3 = daily.airQuality?.o3,
co = daily.airQuality?.cO,
// pollen
alder = daily.pollen?.alder,
ash = daily.pollen?.ash,
birch = daily.pollen?.birch,
chestnut = daily.pollen?.chestnut,
cypress = daily.pollen?.cypress,
grass = daily.pollen?.grass,
hazel = daily.pollen?.hazel,
hornbeam = daily.pollen?.hornbeam,
linden = daily.pollen?.linden,
mold = daily.pollen?.mold,
mugwort = daily.pollen?.mugwort,
oak = daily.pollen?.oak,
olive = daily.pollen?.olive,
plane = daily.pollen?.plane,
plantain = daily.pollen?.plantain,
poplar = daily.pollen?.poplar,
ragweed = daily.pollen?.ragweed,
sorrel = daily.pollen?.sorrel,
tree = daily.pollen?.tree,
urticaceae = daily.pollen?.urticaceae,
willow = daily.pollen?.willow,
// uv.
uvIndex = daily.uV?.index,
sunshineDuration = daily.sunshineDuration,
relativeHumidityAverage = daily.relativeHumidity?.average,
relativeHumidityMin = daily.relativeHumidity?.min,
relativeHumidityMax = daily.relativeHumidity?.max,
dewpointAverage = daily.dewPoint?.average,
dewpointMin = daily.dewPoint?.min,
dewpointMax = daily.dewPoint?.max,
pressureAverage = daily.pressure?.average,
pressureMin = daily.pressure?.min,
pressureMax = daily.pressure?.max,
cloudCoverAverage = daily.cloudCover?.average,
cloudCoverMin = daily.cloudCover?.min,
cloudCoverMax = daily.cloudCover?.max,
visibilityAverage = daily.visibility?.average,
visibilityMin = daily.visibility?.min,
visibilityMax = daily.visibility?.max
)
}
// 3. Save hourly (delete first, then re-add)
hourlysQueries.deleteHourlyListForLocationId(location.formattedId)
weather.hourlyForecast.forEach { hourly ->
hourlysQueries.insert(
locationFormattedId = location.formattedId,
date = hourly.date.time,
daylight = hourly.isDaylight,
weatherCode = hourly.weatherCode,
weatherText = hourly.weatherText,
temperature = hourly.temperature?.temperature,
temperatureSourceFeelsLike = hourly.temperature?.sourceFeelsLike,
temperatureApparent = hourly.temperature?.computedApparent,
temperatureWindChill = hourly.temperature?.computedWindChill,
humidex = hourly.temperature?.computedHumidex,
totalPrecipitation = hourly.precipitation?.total,
thunderstormPrecipitation = hourly.precipitation?.thunderstorm,
rainPrecipitation = hourly.precipitation?.rain,
snowPrecipitation = hourly.precipitation?.snow,
icePrecipitation = hourly.precipitation?.ice,
totalPrecipitationProbability = hourly.precipitationProbability?.total,
thunderstormPrecipitationProbability = hourly.precipitationProbability?.thunderstorm,
rainPrecipitationProbability = hourly.precipitationProbability?.rain,
snowPrecipitationProbability = hourly.precipitationProbability?.snow,
icePrecipitationProbability = hourly.precipitationProbability?.ice,
windDegree = hourly.wind?.degree,
windSpeed = hourly.wind?.speed,
windGusts = hourly.wind?.gusts,
// aqi.
pm25 = hourly.airQuality?.pM25,
pm10 = hourly.airQuality?.pM10,
so2 = hourly.airQuality?.sO2,
no2 = hourly.airQuality?.nO2,
o3 = hourly.airQuality?.o3,
co = hourly.airQuality?.cO,
// uv.
uvIndex = hourly.uV?.index,
// details
relativeHumidity = hourly.relativeHumidity,
dewPoint = hourly.dewPoint,
pressure = hourly.pressure,
cloudCover = hourly.cloudCover,
visibility = hourly.visibility
)
}
// 4. Save minutely (delete first, then re-add)
minutelysQueries.deleteMinutelyListForLocationId(location.formattedId)
weather.minutelyForecast.forEach { minutely ->
minutelysQueries.insert(
locationFormattedId = location.formattedId,
date = minutely.date.time,
minuteInterval = minutely.minuteInterval.toLong(),
precipitationIntensity = minutely.precipitationIntensity
)
}
// 5. Save alerts (delete first, then re-add)
alertsQueries.deleteAlertListForLocationId(location.formattedId)
weather.alertList.forEach { alert ->
alertsQueries.insert(
locationFormattedId = location.formattedId,
alertId = alert.alertId,
startDate = alert.startDate?.time,
endDate = alert.endDate?.time,
headline = alert.headline,
description = alert.description,
instruction = alert.instruction,
source = alert.source,
severity = alert.severity,
color = alert.color.toLong()
)
}
// 6. Save normals (delete first, then re-add)
normalsQueries.deleteNormalsForLocationId(location.formattedId)
weather.normals.forEach { normals ->
normalsQueries.insert(
locationFormattedId = location.formattedId,
month = normals.key.value.toLong(),
temperatureMaxAverage = normals.value.daytimeTemperature,
temperatureMinAverage = normals.value.nighttimeTemperature
)
}
}
}
suspend fun deleteAllWeathers() {
handler.await {
weathersQueries.deleteAllWeathers()
}
}
}

View file

@ -0,0 +1,41 @@
import breezyweather.domain.weather.reference.AlertSeverity;
CREATE TABLE alerts(
_id INTEGER NOT NULL PRIMARY KEY,
location_formatted_id TEXT NOT NULL,
alert_id TEXT NOT NULL,
start_date INTEGER,
end_date INTEGER,
headline TEXT,
description TEXT,
instruction TEXT,
source TEXT,
severity INTEGER AS AlertSeverity NOT NULL,
color INTEGER NOT NULL,
FOREIGN KEY(location_formatted_id) REFERENCES locations (formatted_id)
ON DELETE CASCADE
);
CREATE INDEX alerts_location_formatted_id_index ON alerts(location_formatted_id);
getAlertListByLocationId:
SELECT alert_id, start_date, end_date, headline, description, instruction, source, severity, color
FROM alerts
WHERE location_formatted_id = :locationFormattedId
ORDER BY severity DESC, start_date ASC;
getCurrentAlertsByLocationId:
SELECT alert_id, start_date, end_date, headline, description, instruction, source, severity, color
FROM alerts
WHERE location_formatted_id = :locationFormattedId
AND (end_date IS NULL OR end_date > :now)
ORDER BY severity DESC, start_date ASC;
insert:
INSERT INTO alerts(location_formatted_id, alert_id, start_date, end_date, headline, description, instruction, source, severity, color)
VALUES (:locationFormattedId, :alertId, :startDate, :endDate, :headline, :description, :instruction, :source, :severity, :color);
deleteAlertListForLocationId:
DELETE FROM alerts
WHERE location_formatted_id = :locationFormattedId;

View file

@ -0,0 +1,182 @@
import breezyweather.domain.weather.reference.WeatherCode;
import kotlin.time.Duration;
import org.breezyweather.unit.distance.Distance;
import org.breezyweather.unit.pollen.PollenConcentration;
import org.breezyweather.unit.pollutant.PollutantConcentration;
import org.breezyweather.unit.precipitation.Precipitation;
import org.breezyweather.unit.pressure.Pressure;
import org.breezyweather.unit.ratio.Ratio;
import org.breezyweather.unit.speed.Speed;
import org.breezyweather.unit.temperature.Temperature;
CREATE TABLE dailys(
_id INTEGER NOT NULL PRIMARY KEY,
location_formatted_id TEXT NOT NULL,
date INTEGER NOT NULL,
-- daytime
daytime_weather_text TEXT,
daytime_weather_summary TEXT,
daytime_weather_code TEXT AS WeatherCode,
daytime_temperature INTEGER AS Temperature,
daytime_temperature_source_feels_like INTEGER AS Temperature,
daytime_temperature_apparent INTEGER AS Temperature,
daytime_temperature_wind_chill INTEGER AS Temperature,
daytime_humidex INTEGER AS Temperature,
daytime_total_precipitation INTEGER AS Precipitation,
daytime_thunderstorm_precipitation INTEGER AS Precipitation,
daytime_rain_precipitation INTEGER AS Precipitation,
daytime_snow_precipitation INTEGER AS Precipitation,
daytime_ice_precipitation INTEGER AS Precipitation,
daytime_total_precipitation_probability INTEGER AS Ratio,
daytime_thunderstorm_precipitation_probability INTEGER AS Ratio,
daytime_rain_precipitation_probability INTEGER AS Ratio,
daytime_snow_precipitation_probability INTEGER AS Ratio,
daytime_ice_precipitation_probability INTEGER AS Ratio,
daytime_total_precipitation_duration INTEGER AS Duration,
daytime_thunderstorm_precipitation_duration INTEGER AS Duration,
daytime_rain_precipitation_duration INTEGER AS Duration,
daytime_snow_precipitation_duration INTEGER AS Duration,
daytime_ice_precipitation_duration INTEGER AS Duration,
daytime_wind_degree REAL,
daytime_wind_speed INTEGER AS Speed,
daytime_wind_gusts INTEGER AS Speed,
-- nighttime
nighttime_weather_text TEXT,
nighttime_weather_summary TEXT,
nighttime_weather_code TEXT AS WeatherCode,
nighttime_temperature INTEGER AS Temperature,
nighttime_temperature_source_feels_like INTEGER AS Temperature,
nighttime_temperature_apparent INTEGER AS Temperature,
nighttime_temperature_wind_chill INTEGER AS Temperature,
nighttime_humidex INTEGER AS Temperature,
nighttime_total_precipitation INTEGER AS Precipitation,
nighttime_thunderstorm_precipitation INTEGER AS Precipitation,
nighttime_rain_precipitation INTEGER AS Precipitation,
nighttime_snow_precipitation INTEGER AS Precipitation,
nighttime_ice_precipitation INTEGER AS Precipitation,
nighttime_total_precipitation_probability INTEGER AS Ratio,
nighttime_thunderstorm_precipitation_probability INTEGER AS Ratio,
nighttime_rain_precipitation_probability INTEGER AS Ratio,
nighttime_snow_precipitation_probability INTEGER AS Ratio,
nighttime_ice_precipitation_probability INTEGER AS Ratio,
nighttime_total_precipitation_duration INTEGER AS Duration,
nighttime_thunderstorm_precipitation_duration INTEGER AS Duration,
nighttime_rain_precipitation_duration INTEGER AS Duration,
nighttime_snow_precipitation_duration INTEGER AS Duration,
nighttime_ice_precipitation_duration INTEGER AS Duration,
nighttime_wind_degree REAL,
nighttime_wind_speed INTEGER AS Speed,
nighttime_wind_gusts INTEGER AS Speed,
degree_day_heating INTEGER AS Temperature,
degree_day_cooling INTEGER AS Temperature,
-- sun
sun_rise_date INTEGER,
sun_set_date INTEGER,
-- twilight
twilight_rise_date INTEGER,
twilight_set_date INTEGER,
-- moon
moon_rise_date INTEGER,
moon_set_date INTEGER,
-- moon phase
moon_phase_angle INTEGER,
-- aqi.
pm25 INTEGER AS PollutantConcentration,
pm10 INTEGER AS PollutantConcentration,
so2 INTEGER AS PollutantConcentration,
no2 INTEGER AS PollutantConcentration,
o3 INTEGER AS PollutantConcentration,
co INTEGER AS PollutantConcentration,
-- pollen
alder INTEGER AS PollenConcentration,
ash INTEGER AS PollenConcentration,
birch INTEGER AS PollenConcentration,
chestnut INTEGER AS PollenConcentration,
cypress INTEGER AS PollenConcentration,
grass INTEGER AS PollenConcentration,
hazel INTEGER AS PollenConcentration,
hornbeam INTEGER AS PollenConcentration,
linden INTEGER AS PollenConcentration,
mold INTEGER AS PollenConcentration,
mugwort INTEGER AS PollenConcentration,
oak INTEGER AS PollenConcentration,
olive INTEGER AS PollenConcentration,
plane INTEGER AS PollenConcentration,
plantain INTEGER AS PollenConcentration,
poplar INTEGER AS PollenConcentration,
ragweed INTEGER AS PollenConcentration,
sorrel INTEGER AS PollenConcentration,
tree INTEGER AS PollenConcentration,
urticaceae INTEGER AS PollenConcentration,
willow INTEGER AS PollenConcentration,
-- uv.
uv_index REAL,
sunshine_duration INTEGER AS Duration,
-- relative humidity
relative_humidity_average INTEGER AS Ratio,
relative_humidity_min INTEGER AS Ratio,
relative_humidity_max INTEGER AS Ratio,
-- dewpoint
dewpoint_average INTEGER AS Temperature,
dewpoint_min INTEGER AS Temperature,
dewpoint_max INTEGER AS Temperature,
-- pressure
pressure_average INTEGER AS Pressure,
pressure_min INTEGER AS Pressure,
pressure_max INTEGER AS Pressure,
-- cloud cover
cloud_cover_average INTEGER AS Ratio,
cloud_cover_min INTEGER AS Ratio,
cloud_cover_max INTEGER AS Ratio,
-- visibility
visibility_average INTEGER AS Distance,
visibility_min INTEGER AS Distance,
visibility_max INTEGER AS Distance,
UNIQUE(location_formatted_id, date) ON CONFLICT REPLACE,
FOREIGN KEY(location_formatted_id) REFERENCES locations (formatted_id)
ON DELETE CASCADE
);
CREATE INDEX dailys_location_formatted_id_index ON dailys(location_formatted_id);
getDailyListByLocationId:
SELECT date, daytime_weather_text, daytime_weather_summary, daytime_weather_code, daytime_temperature, daytime_temperature_source_feels_like, daytime_temperature_apparent, daytime_temperature_wind_chill, daytime_humidex, daytime_total_precipitation, daytime_thunderstorm_precipitation, daytime_rain_precipitation, daytime_snow_precipitation, daytime_ice_precipitation, daytime_total_precipitation_probability, daytime_thunderstorm_precipitation_probability, daytime_rain_precipitation_probability, daytime_snow_precipitation_probability, daytime_ice_precipitation_probability, daytime_total_precipitation_duration, daytime_thunderstorm_precipitation_duration, daytime_rain_precipitation_duration, daytime_snow_precipitation_duration, daytime_ice_precipitation_duration, daytime_wind_degree, daytime_wind_speed, daytime_wind_gusts, nighttime_weather_text, nighttime_weather_summary, nighttime_weather_code, nighttime_temperature, nighttime_temperature_source_feels_like, nighttime_temperature_apparent, nighttime_temperature_wind_chill, nighttime_humidex, nighttime_total_precipitation, nighttime_thunderstorm_precipitation, nighttime_rain_precipitation, nighttime_snow_precipitation, nighttime_ice_precipitation, nighttime_total_precipitation_probability, nighttime_thunderstorm_precipitation_probability, nighttime_rain_precipitation_probability, nighttime_snow_precipitation_probability, nighttime_ice_precipitation_probability, nighttime_total_precipitation_duration, nighttime_thunderstorm_precipitation_duration, nighttime_rain_precipitation_duration, nighttime_snow_precipitation_duration, nighttime_ice_precipitation_duration, nighttime_wind_degree, nighttime_wind_speed, nighttime_wind_gusts, degree_day_heating, degree_day_cooling, sun_rise_date, sun_set_date, twilight_rise_date, twilight_set_date, moon_rise_date, moon_set_date, moon_phase_angle, pm25, pm10, so2, no2, o3, co, alder, ash, birch, chestnut, cypress, grass, hazel, hornbeam, linden, mold, mugwort, oak, olive, plane, plantain, poplar, ragweed, sorrel, tree, urticaceae, willow, uv_index, sunshine_duration, relative_humidity_average, relative_humidity_min, relative_humidity_max, dewpoint_average, dewpoint_min, dewpoint_max, pressure_average, pressure_min, pressure_max, cloud_cover_average, cloud_cover_min, cloud_cover_max, visibility_average, visibility_min, visibility_max
FROM dailys
WHERE location_formatted_id = :locationFormattedId
ORDER BY date;
insert:
INSERT INTO dailys(location_formatted_id, date, daytime_weather_text, daytime_weather_summary, daytime_weather_code, daytime_temperature, daytime_temperature_source_feels_like, daytime_temperature_apparent, daytime_temperature_wind_chill, daytime_humidex, daytime_total_precipitation, daytime_thunderstorm_precipitation, daytime_rain_precipitation, daytime_snow_precipitation, daytime_ice_precipitation, daytime_total_precipitation_probability, daytime_thunderstorm_precipitation_probability, daytime_rain_precipitation_probability, daytime_snow_precipitation_probability, daytime_ice_precipitation_probability, daytime_total_precipitation_duration, daytime_thunderstorm_precipitation_duration, daytime_rain_precipitation_duration, daytime_snow_precipitation_duration, daytime_ice_precipitation_duration, daytime_wind_degree, daytime_wind_speed, daytime_wind_gusts, nighttime_weather_text, nighttime_weather_summary, nighttime_weather_code, nighttime_temperature, nighttime_temperature_source_feels_like, nighttime_temperature_apparent, nighttime_temperature_wind_chill, nighttime_humidex, nighttime_total_precipitation, nighttime_thunderstorm_precipitation, nighttime_rain_precipitation, nighttime_snow_precipitation, nighttime_ice_precipitation, nighttime_total_precipitation_probability, nighttime_thunderstorm_precipitation_probability, nighttime_rain_precipitation_probability, nighttime_snow_precipitation_probability, nighttime_ice_precipitation_probability, nighttime_total_precipitation_duration, nighttime_thunderstorm_precipitation_duration, nighttime_rain_precipitation_duration, nighttime_snow_precipitation_duration, nighttime_ice_precipitation_duration, nighttime_wind_degree, nighttime_wind_speed, nighttime_wind_gusts, degree_day_heating, degree_day_cooling, sun_rise_date, sun_set_date, twilight_rise_date, twilight_set_date, moon_rise_date, moon_set_date, moon_phase_angle, pm25, pm10, so2, no2, o3, co, alder, ash, birch, chestnut, cypress, grass, hazel, hornbeam, linden, mold, mugwort, oak, olive, plane, plantain, poplar, ragweed, sorrel, tree, urticaceae, willow, uv_index, sunshine_duration, relative_humidity_average, relative_humidity_min, relative_humidity_max, dewpoint_average, dewpoint_min, dewpoint_max, pressure_average, pressure_min, pressure_max, cloud_cover_average, cloud_cover_min, cloud_cover_max, visibility_average, visibility_min, visibility_max)
VALUES (:locationFormattedId, :date, :daytimeWeatherText, :daytimeweatherSummary, :daytimeWeatherCode, :daytimeTemperature, :daytimeTemperatureSourceFeelsLike, :daytimeTemperatureApparent, :daytimeTemperatureWindChill, :daytimeHumidex, :daytimeTotalPrecipitation, :daytimeThunderstormPrecipitation, :daytimeRainPrecipitation, :daytimeSnowPrecipitation, :daytimeIcePrecipitation, :daytimeTotalPrecipitationProbability, :daytimeThunderstormPrecipitationProbability, :daytimeRainPrecipitationProbability, :daytimeSnowPrecipitationProbability, :daytimeIcePrecipitationProbability, :daytimeTotalPrecipitationDuration, :daytimeThunderstormPrecipitationDuration, :daytimeRainPrecipitationDuration, :daytimeSnowPrecipitationDuration, :daytimeIcePrecipitationDuration, :daytimeWindDegree, :daytimeWindSpeed, :daytimeWindGusts, :nighttimeWeatherText, :nighttimeweatherSummary, :nighttimeWeatherCode, :nighttimeTemperature, :nighttimeTemperatureSourceFeelsLike, :nighttimeTemperatureApparent, :nighttimeTemperatureWindChill, :nighttimeHumidex, :nighttimeTotalPrecipitation, :nighttimeThunderstormPrecipitation, :nighttimeRainPrecipitation, :nighttimeSnowPrecipitation, :nighttimeIcePrecipitation, :nighttimeTotalPrecipitationProbability, :nighttimeThunderstormPrecipitationProbability, :nighttimeRainPrecipitationProbability, :nighttimeSnowPrecipitationProbability, :nighttimeIcePrecipitationProbability, :nighttimeTotalPrecipitationDuration, :nighttimeThunderstormPrecipitationDuration, :nighttimeRainPrecipitationDuration, :nighttimeSnowPrecipitationDuration, :nighttimeIcePrecipitationDuration, :nighttimeWindDegree, :nighttimeWindSpeed, :nighttimeWindGusts, :degreeDayHeating, :degreeDayCooling, :sunRiseDate, :sunSetDate, :twilightRiseDate, :twilightSetDate, :moonRiseDate, :moonSetDate, :moonPhaseAngle, :pm25, :pm10, :so2, :no2, :o3, :co, :alder, :ash, :birch, :chestnut, :cypress, :grass, :hazel, :hornbeam, :linden, :mold, :mugwort, :oak, :olive, :plane, :plantain, :poplar, :ragweed, :sorrel, :tree, :urticaceae, :willow, :uvIndex, :sunshineDuration, :relativeHumidityAverage, :relativeHumidityMin, :relativeHumidityMax, :dewpointAverage, :dewpointMin, :dewpointMax, :pressureAverage, :pressureMin, :pressureMax, :cloudCoverAverage, :cloudCoverMin, :cloudCoverMax, :visibilityAverage, :visibilityMin, :visibilityMax);
deleteDailyListForLocationId:
DELETE FROM dailys
WHERE location_formatted_id = :locationFormattedId;

View file

@ -0,0 +1,79 @@
import breezyweather.domain.weather.reference.WeatherCode;
import kotlin.Boolean;
import org.breezyweather.unit.distance.Distance;
import org.breezyweather.unit.pollutant.PollutantConcentration;
import org.breezyweather.unit.precipitation.Precipitation;
import org.breezyweather.unit.pressure.Pressure;
import org.breezyweather.unit.ratio.Ratio;
import org.breezyweather.unit.speed.Speed;
import org.breezyweather.unit.temperature.Temperature;
CREATE TABLE hourlys(
_id INTEGER NOT NULL PRIMARY KEY,
location_formatted_id TEXT NOT NULL,
date INTEGER NOT NULL,
daylight INTEGER AS Boolean NOT NULL DEFAULT 1,
weather_text TEXT,
weather_code TEXT AS WeatherCode,
temperature INTEGER AS Temperature,
temperature_source_feels_like INTEGER AS Temperature,
temperature_apparent INTEGER AS Temperature,
temperature_wind_chill INTEGER AS Temperature,
humidex INTEGER AS Temperature,
total_precipitation INTEGER AS Precipitation,
thunderstorm_precipitation INTEGER AS Precipitation,
rain_precipitation INTEGER AS Precipitation,
snow_precipitation INTEGER AS Precipitation,
ice_precipitation INTEGER AS Precipitation,
total_precipitation_probability INTEGER AS Ratio,
thunderstorm_precipitation_probability INTEGER AS Ratio,
rain_precipitation_probability INTEGER AS Ratio,
snow_precipitation_probability INTEGER AS Ratio,
ice_precipitation_probability INTEGER AS Ratio,
wind_degree REAL,
wind_speed INTEGER AS Speed,
wind_gusts INTEGER AS Speed,
pm25 INTEGER AS PollutantConcentration,
pm10 INTEGER AS PollutantConcentration,
so2 INTEGER AS PollutantConcentration,
no2 INTEGER AS PollutantConcentration,
o3 INTEGER AS PollutantConcentration,
co INTEGER AS PollutantConcentration,
-- uv.
uvIndex REAL,
-- details
relative_humidity INTEGER AS Ratio,
dew_point INTEGER AS Temperature,
pressure INTEGER AS Pressure,
cloud_cover INTEGER AS Ratio,
visibility INTEGER AS Distance,
UNIQUE(location_formatted_id, date) ON CONFLICT REPLACE,
FOREIGN KEY(location_formatted_id) REFERENCES locations (formatted_id)
ON DELETE CASCADE
);
CREATE INDEX hourlys_location_formatted_id_index ON hourlys(location_formatted_id);
getHourlyListByLocationId:
SELECT date, daylight, weather_text, weather_code, temperature, temperature_source_feels_like, temperature_apparent, temperature_wind_chill, humidex, total_precipitation, thunderstorm_precipitation, rain_precipitation, snow_precipitation, ice_precipitation, total_precipitation_probability, thunderstorm_precipitation_probability, rain_precipitation_probability, snow_precipitation_probability, ice_precipitation_probability, wind_degree, wind_speed, wind_gusts, pm25, pm10, so2, no2, o3, co, uvIndex, relative_humidity, dew_point, pressure, cloud_cover, visibility
FROM hourlys
WHERE location_formatted_id = :locationFormattedId
ORDER BY date;
insert:
INSERT INTO hourlys(location_formatted_id, date, daylight, weather_text, weather_code, temperature, temperature_source_feels_like, temperature_apparent, temperature_wind_chill, humidex, total_precipitation, thunderstorm_precipitation, rain_precipitation, snow_precipitation, ice_precipitation, total_precipitation_probability, thunderstorm_precipitation_probability, rain_precipitation_probability, snow_precipitation_probability, ice_precipitation_probability, wind_degree, wind_speed, wind_gusts, pm25, pm10, so2, no2, o3, co, uvIndex, relative_humidity, dew_point, pressure, cloud_cover, visibility)
VALUES (:locationFormattedId, :date, :daylight, :weatherText, :weatherCode, :temperature, :temperatureSourceFeelsLike, :temperatureApparent, :temperatureWindChill, :humidex, :totalPrecipitation, :thunderstormPrecipitation, :rainPrecipitation, :snowPrecipitation, :icePrecipitation, :totalPrecipitationProbability, :thunderstormPrecipitationProbability, :rainPrecipitationProbability, :snowPrecipitationProbability, :icePrecipitationProbability, :windDegree, :windSpeed, :windGusts, :pm25, :pm10, :so2, :no2, :o3, :co, :uvIndex, :relativeHumidity, :dewPoint, :pressure, :cloudCover, :visibility);
deleteHourlyListForLocationId:
DELETE FROM hourlys
WHERE location_formatted_id = :locationFormattedId;

View file

@ -0,0 +1,40 @@
CREATE TABLE location_parameters(
_id INTEGER NOT NULL PRIMARY KEY,
location_formatted_id TEXT NOT NULL,
source TEXT NOT NULL,
parameter TEXT NOT NULL,
value TEXT NOT NULL,
UNIQUE(location_formatted_id, source, parameter) ON CONFLICT REPLACE,
FOREIGN KEY(location_formatted_id) REFERENCES locations (formatted_id)
ON DELETE CASCADE
);
CREATE INDEX location_parameters_location_formatted_id_index ON location_parameters(location_formatted_id);
getLocationParametersByLocationId:
SELECT source, parameter, value
FROM location_parameters
WHERE location_formatted_id = :locationFormattedId;
insert:
INSERT INTO location_parameters(location_formatted_id, source, parameter, value)
VALUES (:locationFormattedId, :source, :parameter, :value);
updateParameters:
UPDATE location_parameters
SET value = :newValue
WHERE source = :source
AND parameter = :parameter
AND value = :oldValue;
deleteAllNonMatchingParameters:
DELETE FROM location_parameters
WHERE location_formatted_id = :locationFormattedId
AND parameter NOT IN :locationParameterNames;
deleteParameters:
DELETE FROM location_parameters
WHERE source = :source
AND parameter = :parameter
AND value IN :parameterValues;

View file

@ -0,0 +1,145 @@
import java.util.TimeZone;
import kotlin.Boolean;
CREATE TABLE locations(
formatted_id TEXT NOT NULL PRIMARY KEY,
list_order INTEGER NOT NULL,
city_id TEXT,
latitude REAL NOT NULL,
longitude REAL NOT NULL,
timezone TEXT AS TimeZone NOT NULL,
custom_name TEXT,
country TEXT NOT NULL,
country_code TEXT,
admin1 TEXT,
admin1_code TEXT,
admin2 TEXT,
admin2_code TEXT,
admin3 TEXT,
admin3_code TEXT,
admin4 TEXT,
admin4_code TEXT,
city TEXT NOT NULL,
district TEXT,
-- Sources
weather_source TEXT NOT NULL,
current_source TEXT,
air_quality_source TEXT,
pollen_source TEXT,
minutely_source TEXT,
alert_source TEXT,
normals_source TEXT,
reverse_geocoding_source TEXT,
current_position INTEGER AS Boolean NOT NULL DEFAULT 0,
needs_geocode_refresh INTEGER AS Boolean NOT NULL DEFAULT 0,
background_weather_kind TEXT,
background_day_night_type TEXT
);
getLocationById:
SELECT city_id, latitude, longitude, timezone, custom_name, country, country_code, admin1, admin1_code, admin2, admin2_code, admin3, admin3_code, admin4, admin4_code, city, district, weather_source, current_source, air_quality_source, pollen_source, minutely_source, alert_source, normals_source, reverse_geocoding_source, current_position, needs_geocode_refresh, background_weather_kind, background_day_night_type
FROM locations
WHERE formatted_id = :formattedId;
getAllLocations:
SELECT city_id, latitude, longitude, timezone, custom_name, country, country_code, admin1, admin1_code, admin2, admin2_code, admin3, admin3_code, admin4, admin4_code, city, district, weather_source, current_source, air_quality_source, pollen_source, minutely_source, alert_source, normals_source, reverse_geocoding_source, current_position, needs_geocode_refresh, background_weather_kind, background_day_night_type
FROM locations
ORDER BY list_order;
getXLocations:
SELECT city_id, latitude, longitude, timezone, custom_name, country, country_code, admin1, admin1_code, admin2, admin2_code, admin3, admin3_code, admin4, admin4_code, city, district, weather_source, current_source, air_quality_source, pollen_source, minutely_source, alert_source, normals_source, reverse_geocoding_source, current_position, needs_geocode_refresh, background_weather_kind, background_day_night_type
FROM locations
ORDER BY list_order
LIMIT :limit;
getFirstLocation:
SELECT city_id, latitude, longitude, timezone, custom_name, country, country_code, admin1, admin1_code, admin2, admin2_code, admin3, admin3_code, admin4, admin4_code, city, district, weather_source, current_source, air_quality_source, pollen_source, minutely_source, alert_source, normals_source, reverse_geocoding_source, current_position, needs_geocode_refresh, background_weather_kind, background_day_night_type
FROM locations
ORDER BY list_order
LIMIT 1;
insert:
INSERT INTO locations(formatted_id, list_order, city_id, latitude, longitude, timezone, custom_name, country, country_code, admin1, admin1_code, admin2, admin2_code, admin3, admin3_code, admin4, admin4_code, city, district, weather_source, current_source, air_quality_source, pollen_source, minutely_source, alert_source, normals_source, reverse_geocoding_source, current_position, needs_geocode_refresh, background_weather_kind, background_day_night_type)
VALUES (:formattedId, :listOrder, :cityId, :latitude, :longitude, :timezone, :customName, :country, :countryCode, :admin1, :admin1Code, :admin2, :admin2Code, :admin3, :admin3Code, :admin4, :admin4Code, :city, :district, :weatherSource, :currentSource, :airQualitySource, :pollenSource, :minutelySource, :alertSource, :normalsSource, :reverseGeocodingSource, :currentPosition, :needsGeocodeRefresh, :backgroundWeatherKind, :backgroundDayNightType)
ON CONFLICT(formatted_id) DO UPDATE SET
list_order = coalesce(:listOrder, list_order),
city_id = coalesce(:cityId, city_id),
latitude = coalesce(:latitude, latitude),
longitude = coalesce(:longitude, longitude),
timezone = coalesce(:timezone, timezone),
custom_name = coalesce(:customName, custom_name),
country = coalesce(:country, country),
country_code = coalesce(:countryCode, country_code),
admin1 = coalesce(:admin1, admin1),
admin1_code = coalesce(:admin1Code, admin1_code),
admin2 = coalesce(:admin2, admin2),
admin2_code = coalesce(:admin2Code, admin2_code),
admin3 = coalesce(:admin3, admin3),
admin3_code = coalesce(:admin3Code, admin3_code),
admin4 = coalesce(:admin4, admin4),
admin4_code = coalesce(:admin4Code, admin4_code),
city = coalesce(:city, city),
district = coalesce(:district, district),
weather_source = coalesce(:weatherSource, weather_source),
current_source = coalesce(:currentSource, current_source),
air_quality_source = coalesce(:airQualitySource, air_quality_source),
pollen_source = coalesce(:pollenSource, pollen_source),
minutely_source = coalesce(:minutelySource, minutely_source),
alert_source = coalesce(:alertSource, alert_source),
normals_source = coalesce(:normalsSource, normals_source),
reverse_geocoding_source = coalesce(:reverseGeocodingSource, reverse_geocoding_source),
current_position = coalesce(:currentPosition, current_position),
needs_geocode_refresh = coalesce(:needsGeocodeRefresh, needs_geocode_refresh),
background_weather_kind = coalesce(:backgroundWeatherKind, background_weather_kind),
background_day_night_type = coalesce(:backgroundDayNightType, background_day_night_type);
updateFormattedId:
UPDATE locations
SET formatted_id = coalesce(:newFormattedId, formatted_id)
WHERE formatted_id = coalesce(:oldFormattedId, formatted_id);
update:
UPDATE locations
SET city_id = coalesce(:cityId, city_id),
latitude = coalesce(:latitude, latitude),
longitude = coalesce(:longitude, longitude),
timezone = coalesce(:timezone, timezone),
custom_name = coalesce(:customName, custom_name),
country = coalesce(:country, country),
country_code = coalesce(:countryCode, country_code),
admin1 = coalesce(:admin1, admin1),
admin1_code = coalesce(:admin1Code, admin1_code),
admin2 = coalesce(:admin2, admin2),
admin2_code = coalesce(:admin2Code, admin2_code),
admin3 = coalesce(:admin3, admin3),
admin3_code = coalesce(:admin3Code, admin3_code),
admin4 = coalesce(:admin4, admin4),
admin4_code = coalesce(:admin4Code, admin4_code),
city = coalesce(:city, city),
district = coalesce(:district, district),
weather_source = coalesce(:weatherSource, weather_source),
current_source = coalesce(:currentSource, current_source),
air_quality_source = coalesce(:airQualitySource, air_quality_source),
pollen_source = coalesce(:pollenSource, pollen_source),
minutely_source = coalesce(:minutelySource, minutely_source),
alert_source = coalesce(:alertSource, alert_source),
normals_source = coalesce(:normalsSource, normals_source),
reverse_geocoding_source = coalesce(:reverseGeocodingSource, reverse_geocoding_source),
current_position = coalesce(:currentPosition, current_position),
needs_geocode_refresh = coalesce(:needsGeocodeRefresh, needs_geocode_refresh),
background_weather_kind = coalesce(:backgroundWeatherKind, background_weather_kind),
background_day_night_type = coalesce(:backgroundDayNightType, background_day_night_type)
WHERE formatted_id = coalesce(:formattedId, formatted_id);
deleteLocation:
DELETE FROM locations
WHERE formatted_id = :formattedId;
deleteAllNonMatchingLocations:
DELETE FROM locations
WHERE formatted_id NOT IN :formattedIds;

View file

@ -0,0 +1,29 @@
import org.breezyweather.unit.precipitation.Precipitation;
CREATE TABLE minutelys(
_id INTEGER NOT NULL PRIMARY KEY,
location_formatted_id TEXT NOT NULL,
date INTEGER NOT NULL,
minute_interval INTEGER NOT NULL,
precipitation_intensity INTEGER AS Precipitation,
FOREIGN KEY(location_formatted_id) REFERENCES locations (formatted_id)
ON DELETE CASCADE
);
CREATE INDEX minutelys_location_formatted_id_index ON minutelys(location_formatted_id);
getMinutelyListByLocationId:
SELECT date, minute_interval, precipitation_intensity
FROM minutelys
WHERE location_formatted_id = :locationFormattedId
ORDER BY date;
insert:
INSERT INTO minutelys(location_formatted_id, date, minute_interval, precipitation_intensity)
VALUES (:locationFormattedId, :date, :minuteInterval, :precipitationIntensity);
deleteMinutelyListForLocationId:
DELETE FROM minutelys
WHERE location_formatted_id = :locationFormattedId;

View file

@ -0,0 +1,30 @@
import org.breezyweather.unit.temperature.Temperature;
CREATE TABLE normals(
_id INTEGER NOT NULL PRIMARY KEY,
location_formatted_id TEXT NOT NULL,
month INTEGER NOT NULL,
temperature_max_average INTEGER AS Temperature,
temperature_min_average INTEGER AS Temperature,
UNIQUE(location_formatted_id, month) ON CONFLICT REPLACE,
FOREIGN KEY(location_formatted_id) REFERENCES locations (formatted_id)
ON DELETE CASCADE
);
CREATE INDEX normals_location_formatted_id_index ON normals(location_formatted_id);
getNormalsByLocationId:
SELECT month, temperature_max_average, temperature_min_average
FROM normals
WHERE location_formatted_id = :locationFormattedId
ORDER BY month;
insert:
INSERT INTO normals(location_formatted_id, month, temperature_max_average, temperature_min_average)
VALUES (:locationFormattedId, :month, :temperatureMaxAverage, :temperatureMinAverage);
deleteNormalsForLocationId:
DELETE FROM normals
WHERE location_formatted_id = :locationFormattedId;

View file

@ -0,0 +1,118 @@
import breezyweather.domain.weather.reference.WeatherCode;
import org.breezyweather.unit.distance.Distance;
import org.breezyweather.unit.pollutant.PollutantConcentration;
import org.breezyweather.unit.pressure.Pressure;
import org.breezyweather.unit.ratio.Ratio;
import org.breezyweather.unit.speed.Speed;
import org.breezyweather.unit.temperature.Temperature;
CREATE TABLE weathers(
_id INTEGER NOT NULL PRIMARY KEY,
location_formatted_id TEXT NOT NULL,
-- Base
refresh_time INTEGER,
main_update_time INTEGER,
current_update_time INTEGER,
air_quality_update_time INTEGER,
pollen_update_time INTEGER,
minutely_update_time INTEGER,
alerts_update_time INTEGER,
normals_update_time INTEGER,
normals_update_latitude REAL NOT NULL DEFAULT 0.0,
normals_update_longitude REAL NOT NULL DEFAULT 0.0,
-- Current
weather_text TEXT,
weather_code TEXT AS WeatherCode,
temperature INTEGER AS Temperature,
temperature_source_feels_like INTEGER AS Temperature,
temperature_apparent INTEGER AS Temperature,
temperature_wind_chill INTEGER AS Temperature,
humidex INTEGER AS Temperature,
wind_degree REAL,
wind_speed INTEGER AS Speed,
wind_gusts INTEGER AS Speed,
uv_index REAL,
pm25 INTEGER AS PollutantConcentration,
pm10 INTEGER AS PollutantConcentration,
so2 INTEGER AS PollutantConcentration,
no2 INTEGER AS PollutantConcentration,
o3 INTEGER AS PollutantConcentration,
co INTEGER AS PollutantConcentration,
relative_humidity INTEGER AS Ratio,
dew_point INTEGER AS Temperature,
pressure INTEGER AS Pressure,
visibility INTEGER AS Distance,
cloud_cover INTEGER AS Ratio,
ceiling INTEGER AS Distance,
daily_forecast TEXT,
hourly_forecast TEXT,
UNIQUE(location_formatted_id) ON CONFLICT REPLACE,
FOREIGN KEY(location_formatted_id) REFERENCES locations (formatted_id)
ON DELETE CASCADE
);
CREATE INDEX weathers_location_formatted_id_index ON weathers(location_formatted_id);
getWeatherByLocationId:
SELECT refresh_time, main_update_time, current_update_time, air_quality_update_time, pollen_update_time, minutely_update_time, alerts_update_time, normals_update_time, normals_update_latitude, normals_update_longitude, weather_text, weather_code, temperature, temperature_source_feels_like, temperature_apparent, temperature_wind_chill, humidex, wind_degree, wind_speed, wind_gusts, uv_index, pm25, pm10, so2, no2, o3, co, relative_humidity, dew_point, pressure, visibility, cloud_cover, ceiling, daily_forecast, hourly_forecast
FROM weathers
WHERE location_formatted_id = :locationFormattedId;
insert:
INSERT INTO weathers(location_formatted_id, refresh_time, main_update_time, current_update_time, air_quality_update_time, pollen_update_time, minutely_update_time, alerts_update_time, normals_update_time, normals_update_latitude, normals_update_longitude, weather_text, weather_code, temperature, temperature_source_feels_like, temperature_apparent, temperature_wind_chill, humidex, wind_degree, wind_speed, wind_gusts, uv_index, pm25, pm10, so2, no2, o3, co, relative_humidity, dew_point, pressure, visibility, cloud_cover, ceiling, daily_forecast, hourly_forecast)
VALUES (:locationFormattedId, :refreshTime, :mainUpdateTime, :currentUpdateTime, :airQualityUpdateTime, :pollenUpdateTime, :minutelyUpdateTime, :alertsUpdateTime, :normalsUpdateTime, :normalsUpdateLatitude, :normalsUpdateLongitude, :weatherText, :weatherCode, :temperature, :temperatureSourceFeelsLike, :temperatureApparent, :temperatureWindChill, :humidex, :windDegree, :windSpeed, :windGusts, :uvIndex, :pm25, :pm10, :so2, :no2, :o3, :co, :relativeHumidity, :dewPoint, :pressure, :visibility, :cloudCover, :ceiling, :dailyForecast, :hourlyForecast);
update:
UPDATE weathers SET
location_formatted_id = coalesce(:locationFormattedId, location_formatted_id),
refresh_time = coalesce(:refreshTime, refresh_time),
main_update_time = coalesce(:mainUpdateTime, main_update_time),
current_update_time = coalesce(:currentUpdateTime, current_update_time),
air_quality_update_time = coalesce(:airQualityUpdateTime, air_quality_update_time),
pollen_update_time = coalesce(:pollenUpdateTime, pollen_update_time),
minutely_update_time = coalesce(:minutelyUpdateTime, minutely_update_time),
alerts_update_time = coalesce(:alertsUpdateTime, alerts_update_time),
normals_update_time = coalesce(:normalsUpdateTime, normals_update_time),
normals_update_latitude = coalesce(:normalsUpdateTime, normals_update_latitude),
normals_update_longitude = coalesce(:normalsUpdateTime, normals_update_longitude),
weather_text = coalesce(:weatherText, weather_text),
weather_code = coalesce(:weatherCode, weather_code),
temperature = coalesce(:temperature, temperature),
temperature_source_feels_like = coalesce(:temperatureSourceFeelsLike, temperature_source_feels_like),
temperature_apparent = coalesce(:apparentTemperature, temperature_apparent),
temperature_wind_chill = coalesce(:windChillTemperature, temperature_wind_chill),
humidex = coalesce(:humidex, humidex),
wind_degree = coalesce(:windDegree, wind_degree),
wind_speed = coalesce(:windSpeed, wind_speed),
wind_gusts = coalesce(:windGusts, wind_gusts),
uv_index = coalesce(:uvIndex, uv_index),
pm25 = coalesce(:pm25, pm25),
pm10 = coalesce(:pm10, pm10),
so2 = coalesce(:so2, so2),
no2 = coalesce(:no2, no2),
o3 = coalesce(:o3, o3),
co = coalesce(:co, co),
relative_humidity = coalesce(:relativeHumidity, relative_humidity),
dew_point = coalesce(:dewPoint, dew_point),
pressure = coalesce(:pressure, pressure),
visibility = coalesce(:visibility, visibility),
cloud_cover = coalesce(:cloudCover, cloud_cover),
ceiling = coalesce(:ceiling, ceiling),
daily_forecast = coalesce(:dailyForecast, daily_forecast),
hourly_forecast = coalesce(:hourlyForecast, hourly_forecast)
WHERE location_formatted_id = :locationFormattedId;
deleteWeatherForLocationId:
DELETE FROM weathers
WHERE location_formatted_id = :locationFormattedId;
deleteAllWeathers:
DELETE FROM weathers;

View file

@ -0,0 +1,38 @@
ALTER TABLE dailys
ADD COLUMN ash INTEGER;
ALTER TABLE dailys
ADD COLUMN chestnut INTEGER;
ALTER TABLE dailys
ADD COLUMN cypress INTEGER;
ALTER TABLE dailys
ADD COLUMN hazel INTEGER;
ALTER TABLE dailys
ADD COLUMN hornbeam INTEGER;
ALTER TABLE dailys
ADD COLUMN linden INTEGER;
ALTER TABLE dailys
ADD COLUMN oak INTEGER;
ALTER TABLE dailys
ADD COLUMN plane INTEGER;
ALTER TABLE dailys
ADD COLUMN plantain INTEGER;
ALTER TABLE dailys
ADD COLUMN poplar INTEGER;
ALTER TABLE dailys
ADD COLUMN sorrel INTEGER;
ALTER TABLE dailys
ADD COLUMN urticaceae INTEGER;
ALTER TABLE dailys
ADD COLUMN willow INTEGER;

View file

@ -0,0 +1,5 @@
ALTER TABLE dailys
ADD COLUMN twilight_rise_date INTEGER;
ALTER TABLE dailys
ADD COLUMN twilight_set_date INTEGER;

View file

@ -0,0 +1,37 @@
ALTER TABLE dailys
DROP COLUMN daytime_real_feel_shader_temperature;
ALTER TABLE dailys
DROP COLUMN nighttime_real_feel_shader_temperature;
ALTER TABLE dailys
RENAME COLUMN daytime_real_feel_temperature TO daytime_source_feels_like_temperature;
ALTER TABLE dailys
RENAME COLUMN nighttime_real_feel_temperature TO nighttime_source_feels_like_temperature;
ALTER TABLE dailys
RENAME COLUMN daytime_wet_bulb_temperature TO daytime_humidex;
ALTER TABLE dailys
RENAME COLUMN nighttime_wet_bulb_temperature TO nighttime_humidex;
ALTER TABLE hourlys
DROP COLUMN real_feel_shader_temperature;
ALTER TABLE hourlys
RENAME COLUMN real_feel_temperature TO source_feels_like_temperature;
ALTER TABLE hourlys
RENAME COLUMN wet_bulb_temperature TO humidex;
ALTER TABLE weathers
DROP COLUMN real_feel_shader_temperature;
ALTER TABLE weathers
RENAME COLUMN real_feel_temperature TO source_feels_like_temperature;
ALTER TABLE weathers
RENAME COLUMN wet_bulb_temperature TO humidex;

View file

@ -0,0 +1,8 @@
ALTER TABLE weathers
DROP COLUMN normals_month;
ALTER TABLE weathers
DROP COLUMN normals_daytime_temperature;
ALTER TABLE weathers
DROP COLUMN normals_nighttime_temperature;

View file

@ -0,0 +1,14 @@
CREATE TABLE normals(
_id INTEGER NOT NULL PRIMARY KEY,
location_formatted_id TEXT NOT NULL,
month INTEGER NOT NULL,
daytime_temperature REAL,
nighttime_temperature REAL,
UNIQUE(location_formatted_id, month) ON CONFLICT REPLACE,
FOREIGN KEY(location_formatted_id) REFERENCES locations (formatted_id)
ON DELETE CASCADE
);
CREATE INDEX normals_location_formatted_id_index ON normals(location_formatted_id);

View file

@ -0,0 +1,5 @@
ALTER TABLE weathers
ADD COLUMN normals_update_latitude REAL NOT NULL DEFAULT 0.0;
ALTER TABLE weathers
ADD COLUMN normals_update_longitude REAL NOT NULL DEFAULT 0.0;

View file

@ -0,0 +1,44 @@
ALTER TABLE dailys
ADD COLUMN relative_humidity_average REAL;
ALTER TABLE dailys
ADD COLUMN relative_humidity_min REAL;
ALTER TABLE dailys
ADD COLUMN relative_humidity_max REAL;
ALTER TABLE dailys
ADD COLUMN dewpoint_average REAL;
ALTER TABLE dailys
ADD COLUMN dewpoint_min REAL;
ALTER TABLE dailys
ADD COLUMN dewpoint_max REAL;
ALTER TABLE dailys
ADD COLUMN pressure_average REAL;
ALTER TABLE dailys
ADD COLUMN pressure_min REAL;
ALTER TABLE dailys
ADD COLUMN pressure_max REAL;
ALTER TABLE dailys
ADD COLUMN cloud_cover_average INTEGER;
ALTER TABLE dailys
ADD COLUMN cloud_cover_min INTEGER;
ALTER TABLE dailys
ADD COLUMN cloud_cover_max INTEGER;
ALTER TABLE dailys
ADD COLUMN visibility_average REAL;
ALTER TABLE dailys
ADD COLUMN visibility_min REAL;
ALTER TABLE dailys
ADD COLUMN visibility_max REAL;

View file

@ -0,0 +1,5 @@
ALTER TABLE dailys
DROP COLUMN daytime_cloud_cover;
ALTER TABLE dailys
DROP COLUMN nighttime_cloud_cover;

View file

@ -0,0 +1,5 @@
ALTER TABLE dailys
RENAME COLUMN daytime_weather_phase TO daytime_weather_summary;
ALTER TABLE dailys
RENAME COLUMN nighttime_weather_phase TO nighttime_weather_summary;

View file

@ -0,0 +1,29 @@
ALTER TABLE weathers
DROP COLUMN pressure;
ALTER TABLE weathers
ADD COLUMN pressure INTEGER;
ALTER TABLE dailys
DROP COLUMN pressure_average;
ALTER TABLE dailys
ADD COLUMN pressure_average INTEGER;
ALTER TABLE dailys
DROP COLUMN pressure_min;
ALTER TABLE dailys
ADD COLUMN pressure_min INTEGER;
ALTER TABLE dailys
DROP COLUMN pressure_max;
ALTER TABLE dailys
ADD COLUMN pressure_max INTEGER;
ALTER TABLE hourlys
DROP COLUMN pressure;
ALTER TABLE hourlys
ADD COLUMN pressure INTEGER;

View file

@ -0,0 +1,65 @@
ALTER TABLE dailys
DROP COLUMN daytime_total_precipitation_duration;
ALTER TABLE dailys
ADD COLUMN daytime_total_precipitation_duration INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_thunderstorm_precipitation_duration;
ALTER TABLE dailys
ADD COLUMN daytime_thunderstorm_precipitation_duration INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_rain_precipitation_duration;
ALTER TABLE dailys
ADD COLUMN daytime_rain_precipitation_duration INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_snow_precipitation_duration;
ALTER TABLE dailys
ADD COLUMN daytime_snow_precipitation_duration INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_ice_precipitation_duration;
ALTER TABLE dailys
ADD COLUMN daytime_ice_precipitation_duration INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_total_precipitation_duration;
ALTER TABLE dailys
ADD COLUMN nighttime_total_precipitation_duration INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_thunderstorm_precipitation_duration;
ALTER TABLE dailys
ADD COLUMN nighttime_thunderstorm_precipitation_duration INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_rain_precipitation_duration;
ALTER TABLE dailys
ADD COLUMN nighttime_rain_precipitation_duration INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_snow_precipitation_duration;
ALTER TABLE dailys
ADD COLUMN nighttime_snow_precipitation_duration INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_ice_precipitation_duration;
ALTER TABLE dailys
ADD COLUMN nighttime_ice_precipitation_duration INTEGER;
ALTER TABLE dailys
DROP COLUMN sunshine_duration;
ALTER TABLE dailys
ADD COLUMN sunshine_duration INTEGER;

View file

@ -0,0 +1,2 @@
ALTER TABLE dailys
RENAME COLUMN hours_of_sun TO sunshine_duration;

View file

@ -0,0 +1,35 @@
ALTER TABLE weathers
DROP COLUMN visibility;
ALTER TABLE weathers
ADD COLUMN visibility INTEGER;
ALTER TABLE weathers
DROP COLUMN ceiling;
ALTER TABLE weathers
ADD COLUMN ceiling INTEGER;
ALTER TABLE dailys
DROP COLUMN visibility_average;
ALTER TABLE dailys
ADD COLUMN visibility_average INTEGER;
ALTER TABLE dailys
DROP COLUMN visibility_min;
ALTER TABLE dailys
ADD COLUMN visibility_min INTEGER;
ALTER TABLE dailys
DROP COLUMN visibility_max;
ALTER TABLE dailys
ADD COLUMN visibility_max INTEGER;
ALTER TABLE hourlys
DROP COLUMN visibility;
ALTER TABLE hourlys
ADD COLUMN visibility INTEGER;

View file

@ -0,0 +1,95 @@
ALTER TABLE dailys
DROP COLUMN daytime_total_precipitation;
ALTER TABLE dailys
ADD COLUMN daytime_total_precipitation INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_thunderstorm_precipitation;
ALTER TABLE dailys
ADD COLUMN daytime_thunderstorm_precipitation INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_rain_precipitation;
ALTER TABLE dailys
ADD COLUMN daytime_rain_precipitation INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_snow_precipitation;
ALTER TABLE dailys
ADD COLUMN daytime_snow_precipitation INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_ice_precipitation;
ALTER TABLE dailys
ADD COLUMN daytime_ice_precipitation INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_total_precipitation;
ALTER TABLE dailys
ADD COLUMN nighttime_total_precipitation INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_thunderstorm_precipitation;
ALTER TABLE dailys
ADD COLUMN nighttime_thunderstorm_precipitation INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_rain_precipitation;
ALTER TABLE dailys
ADD COLUMN nighttime_rain_precipitation INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_snow_precipitation;
ALTER TABLE dailys
ADD COLUMN nighttime_snow_precipitation INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_ice_precipitation;
ALTER TABLE dailys
ADD COLUMN nighttime_ice_precipitation INTEGER;
ALTER TABLE hourlys
DROP COLUMN total_precipitation;
ALTER TABLE hourlys
ADD COLUMN total_precipitation INTEGER;
ALTER TABLE hourlys
DROP COLUMN thunderstorm_precipitation;
ALTER TABLE hourlys
ADD COLUMN thunderstorm_precipitation INTEGER;
ALTER TABLE hourlys
DROP COLUMN rain_precipitation;
ALTER TABLE hourlys
ADD COLUMN rain_precipitation INTEGER;
ALTER TABLE hourlys
DROP COLUMN snow_precipitation;
ALTER TABLE hourlys
ADD COLUMN snow_precipitation INTEGER;
ALTER TABLE hourlys
DROP COLUMN ice_precipitation;
ALTER TABLE hourlys
ADD COLUMN ice_precipitation INTEGER;
ALTER TABLE minutelys
DROP COLUMN precipitation_intensity;
ALTER TABLE minutelys
ADD COLUMN precipitation_intensity INTEGER;

View file

@ -0,0 +1,107 @@
ALTER TABLE dailys
DROP COLUMN pm25;
ALTER TABLE dailys
ADD COLUMN pm25 INTEGER;
ALTER TABLE dailys
DROP COLUMN pm10;
ALTER TABLE dailys
ADD COLUMN pm10 INTEGER;
ALTER TABLE dailys
DROP COLUMN so2;
ALTER TABLE dailys
ADD COLUMN so2 INTEGER;
ALTER TABLE dailys
DROP COLUMN no2;
ALTER TABLE dailys
ADD COLUMN no2 INTEGER;
ALTER TABLE dailys
DROP COLUMN o3;
ALTER TABLE dailys
ADD COLUMN o3 INTEGER;
ALTER TABLE dailys
DROP COLUMN co;
ALTER TABLE dailys
ADD COLUMN co INTEGER;
ALTER TABLE hourlys
DROP COLUMN pm25;
ALTER TABLE hourlys
ADD COLUMN pm25 INTEGER;
ALTER TABLE hourlys
DROP COLUMN pm10;
ALTER TABLE hourlys
ADD COLUMN pm10 INTEGER;
ALTER TABLE hourlys
DROP COLUMN so2;
ALTER TABLE hourlys
ADD COLUMN so2 INTEGER;
ALTER TABLE hourlys
DROP COLUMN no2;
ALTER TABLE hourlys
ADD COLUMN no2 INTEGER;
ALTER TABLE hourlys
DROP COLUMN o3;
ALTER TABLE hourlys
ADD COLUMN o3 INTEGER;
ALTER TABLE hourlys
DROP COLUMN co;
ALTER TABLE hourlys
ADD COLUMN co INTEGER;
ALTER TABLE weathers
DROP COLUMN pm25;
ALTER TABLE weathers
ADD COLUMN pm25 INTEGER;
ALTER TABLE weathers
DROP COLUMN pm10;
ALTER TABLE weathers
ADD COLUMN pm10 INTEGER;
ALTER TABLE weathers
DROP COLUMN so2;
ALTER TABLE weathers
ADD COLUMN so2 INTEGER;
ALTER TABLE weathers
DROP COLUMN no2;
ALTER TABLE weathers
ADD COLUMN no2 INTEGER;
ALTER TABLE weathers
DROP COLUMN o3;
ALTER TABLE weathers
ADD COLUMN o3 INTEGER;
ALTER TABLE weathers
DROP COLUMN co;
ALTER TABLE weathers
ADD COLUMN co INTEGER;

View file

@ -0,0 +1,49 @@
ALTER TABLE dailys
DROP COLUMN daytime_wind_speed;
ALTER TABLE dailys
ADD COLUMN daytime_wind_speed INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_wind_gusts;
ALTER TABLE dailys
ADD COLUMN daytime_wind_gusts INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_wind_speed;
ALTER TABLE dailys
ADD COLUMN nighttime_wind_speed INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_wind_gusts;
ALTER TABLE dailys
ADD COLUMN nighttime_wind_gusts INTEGER;
ALTER TABLE hourlys
DROP COLUMN wind_speed;
ALTER TABLE hourlys
ADD COLUMN wind_speed INTEGER;
ALTER TABLE hourlys
DROP COLUMN wind_gusts;
ALTER TABLE hourlys
ADD COLUMN wind_gusts INTEGER;
ALTER TABLE weathers
DROP COLUMN wind_speed;
ALTER TABLE weathers
ADD COLUMN wind_speed INTEGER;
ALTER TABLE weathers
DROP COLUMN wind_gusts;
ALTER TABLE weathers
ADD COLUMN wind_gusts INTEGER;

View file

@ -0,0 +1,179 @@
ALTER TABLE dailys
DROP COLUMN daytime_temperature;
ALTER TABLE dailys
ADD COLUMN daytime_temperature INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_source_feels_like_temperature;
ALTER TABLE dailys
ADD COLUMN daytime_temperature_source_feels_like INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_apparent_temperature;
ALTER TABLE dailys
ADD COLUMN daytime_temperature_apparent INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_wind_chill_temperature;
ALTER TABLE dailys
ADD COLUMN daytime_temperature_wind_chill INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_humidex;
ALTER TABLE dailys
ADD COLUMN daytime_humidex INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_temperature;
ALTER TABLE dailys
ADD COLUMN nighttime_temperature INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_source_feels_like_temperature;
ALTER TABLE dailys
ADD COLUMN nighttime_temperature_source_feels_like INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_apparent_temperature;
ALTER TABLE dailys
ADD COLUMN nighttime_temperature_apparent INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_wind_chill_temperature;
ALTER TABLE dailys
ADD COLUMN nighttime_temperature_wind_chill INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_humidex;
ALTER TABLE dailys
ADD COLUMN nighttime_humidex INTEGER;
ALTER TABLE dailys
DROP COLUMN degree_day_heating;
ALTER TABLE dailys
ADD COLUMN degree_day_heating INTEGER;
ALTER TABLE dailys
DROP COLUMN degree_day_cooling;
ALTER TABLE dailys
ADD COLUMN degree_day_cooling INTEGER;
ALTER TABLE dailys
DROP COLUMN dewpoint_average;
ALTER TABLE dailys
ADD COLUMN dewpoint_average INTEGER;
ALTER TABLE dailys
DROP COLUMN dewpoint_min;
ALTER TABLE dailys
ADD COLUMN dewpoint_min INTEGER;
ALTER TABLE dailys
DROP COLUMN dewpoint_max;
ALTER TABLE dailys
ADD COLUMN dewpoint_max INTEGER;
ALTER TABLE hourlys
DROP COLUMN temperature;
ALTER TABLE hourlys
ADD COLUMN temperature INTEGER;
ALTER TABLE hourlys
DROP COLUMN source_feels_like_temperature;
ALTER TABLE hourlys
ADD COLUMN temperature_source_feels_like INTEGER;
ALTER TABLE hourlys
DROP COLUMN apparent_temperature;
ALTER TABLE hourlys
ADD COLUMN temperature_apparent INTEGER;
ALTER TABLE hourlys
DROP COLUMN wind_chill_temperature;
ALTER TABLE hourlys
ADD COLUMN temperature_wind_chill INTEGER;
ALTER TABLE hourlys
DROP COLUMN humidex;
ALTER TABLE hourlys
ADD COLUMN humidex INTEGER;
ALTER TABLE hourlys
DROP COLUMN dew_point;
ALTER TABLE hourlys
ADD COLUMN dew_point INTEGER;
ALTER TABLE weathers
DROP COLUMN temperature;
ALTER TABLE weathers
ADD COLUMN temperature INTEGER;
ALTER TABLE weathers
DROP COLUMN source_feels_like_temperature;
ALTER TABLE weathers
ADD COLUMN temperature_source_feels_like INTEGER;
ALTER TABLE weathers
DROP COLUMN apparent_temperature;
ALTER TABLE weathers
ADD COLUMN temperature_apparent INTEGER;
ALTER TABLE weathers
DROP COLUMN wind_chill_temperature;
ALTER TABLE weathers
ADD COLUMN temperature_wind_chill INTEGER;
ALTER TABLE weathers
DROP COLUMN humidex;
ALTER TABLE weathers
ADD COLUMN humidex INTEGER;
ALTER TABLE weathers
DROP COLUMN dew_point;
ALTER TABLE weathers
ADD COLUMN dew_point INTEGER;
ALTER TABLE normals
DROP COLUMN daytime_temperature;
ALTER TABLE normals
ADD COLUMN temperature_max_average INTEGER;
ALTER TABLE normals
DROP COLUMN nighttime_temperature;
ALTER TABLE normals
ADD COLUMN temperature_min_average INTEGER;

View file

@ -0,0 +1,123 @@
ALTER TABLE dailys
DROP COLUMN daytime_total_precipitation_probability;
ALTER TABLE dailys
ADD COLUMN daytime_total_precipitation_probability INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_thunderstorm_precipitation_probability;
ALTER TABLE dailys
ADD COLUMN daytime_thunderstorm_precipitation_probability INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_rain_precipitation_probability;
ALTER TABLE dailys
ADD COLUMN daytime_rain_precipitation_probability INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_snow_precipitation_probability;
ALTER TABLE dailys
ADD COLUMN daytime_snow_precipitation_probability INTEGER;
ALTER TABLE dailys
DROP COLUMN daytime_ice_precipitation_probability;
ALTER TABLE dailys
ADD COLUMN daytime_ice_precipitation_probability INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_total_precipitation_probability;
ALTER TABLE dailys
ADD COLUMN nighttime_total_precipitation_probability INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_thunderstorm_precipitation_probability;
ALTER TABLE dailys
ADD COLUMN nighttime_thunderstorm_precipitation_probability INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_rain_precipitation_probability;
ALTER TABLE dailys
ADD COLUMN nighttime_rain_precipitation_probability INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_snow_precipitation_probability;
ALTER TABLE dailys
ADD COLUMN nighttime_snow_precipitation_probability INTEGER;
ALTER TABLE dailys
DROP COLUMN nighttime_ice_precipitation_probability;
ALTER TABLE dailys
ADD COLUMN nighttime_ice_precipitation_probability INTEGER;
ALTER TABLE dailys
DROP COLUMN relative_humidity_average;
ALTER TABLE dailys
ADD COLUMN relative_humidity_average INTEGER;
ALTER TABLE dailys
DROP COLUMN relative_humidity_min;
ALTER TABLE dailys
ADD COLUMN relative_humidity_min INTEGER;
ALTER TABLE dailys
DROP COLUMN relative_humidity_max;
ALTER TABLE dailys
ADD COLUMN relative_humidity_max INTEGER;
ALTER TABLE hourlys
DROP COLUMN total_precipitation_probability;
ALTER TABLE hourlys
ADD COLUMN total_precipitation_probability INTEGER;
ALTER TABLE hourlys
DROP COLUMN thunderstorm_precipitation_probability;
ALTER TABLE hourlys
ADD COLUMN thunderstorm_precipitation_probability INTEGER;
ALTER TABLE hourlys
DROP COLUMN rain_precipitation_probability;
ALTER TABLE hourlys
ADD COLUMN rain_precipitation_probability INTEGER;
ALTER TABLE hourlys
DROP COLUMN snow_precipitation_probability;
ALTER TABLE hourlys
ADD COLUMN snow_precipitation_probability INTEGER;
ALTER TABLE hourlys
DROP COLUMN ice_precipitation_probability;
ALTER TABLE hourlys
ADD COLUMN ice_precipitation_probability INTEGER;
ALTER TABLE hourlys
DROP COLUMN relative_humidity;
ALTER TABLE hourlys
ADD COLUMN relative_humidity INTEGER;
ALTER TABLE weathers
DROP COLUMN relative_humidity;
ALTER TABLE weathers
ADD COLUMN relative_humidity INTEGER;

View file

@ -0,0 +1,14 @@
ALTER TABLE alerts
DROP COLUMN description;
ALTER TABLE alerts
ADD COLUMN headline TEXT;
ALTER TABLE alerts
RENAME COLUMN content TO description;
ALTER TABLE alerts
ADD COLUMN instruction TEXT;
ALTER TABLE alerts
RENAME COLUMN priority TO severity;

View file

@ -0,0 +1,5 @@
ALTER TABLE locations
ADD COLUMN background_weather_kind TEXT;
ALTER TABLE locations
ADD COLUMN background_day_night_type TEXT;

View file

@ -0,0 +1,2 @@
ALTER TABLE alerts
ADD COLUMN source TEXT;

View file

@ -0,0 +1,23 @@
ALTER TABLE locations
RENAME COLUMN province TO admin2;
ALTER TABLE locations
RENAME COLUMN province_code TO admin2_code;
ALTER TABLE locations
ADD COLUMN admin1 TEXT;
ALTER TABLE locations
ADD COLUMN admin1_code TEXT;
ALTER TABLE locations
ADD COLUMN admin3 TEXT;
ALTER TABLE locations
ADD COLUMN admin3_code TEXT;
ALTER TABLE locations
ADD COLUMN admin4 TEXT;
ALTER TABLE locations
ADD COLUMN admin4_code TEXT;

View file

@ -0,0 +1,5 @@
ALTER TABLE locations
ADD COLUMN current_source TEXT;
ALTER TABLE weathers
ADD COLUMN current_update_time INTEGER;

View file

@ -0,0 +1,2 @@
ALTER TABLE locations
ADD COLUMN custom_name TEXT;

View file

@ -0,0 +1,2 @@
ALTER TABLE locations
ADD COLUMN reverse_geocoding_source TEXT;