diff --git a/db/whatsappbot.mv.db b/db/whatsappbot.mv.db index 291090a..a123c36 100644 Binary files a/db/whatsappbot.mv.db and b/db/whatsappbot.mv.db differ diff --git a/db/whatsappbot.trace.db b/db/whatsappbot.trace.db index 3d9c853..281e5a4 100644 --- a/db/whatsappbot.trace.db +++ b/db/whatsappbot.trace.db @@ -943,3 +943,339 @@ org.h2.jdbc.JdbcSQLNonTransientConnectionException: Database is already closed ( at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:480) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:57) at java.base/java.lang.Thread.run(Thread.java:1447) +2026-08-20 04:52:46.342121Z database: flush +org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /opt/projects/RITz-whatsapp-bot/db/whatsappbot.mv.db [2.4.240/7]" [50000-240] + at org.h2.message.DbException.get(DbException.java:212) + at org.h2.message.DbException.convert(DbException.java:407) + at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) + at org.h2.mvstore.MVStore.handleException(MVStore.java:1675) + at org.h2.mvstore.MVStore.panic(MVStore.java:502) + at org.h2.mvstore.MVStore.(MVStore.java:296) + at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2164) + at org.h2.mvstore.db.Store.(Store.java:133) + at org.h2.engine.Database.(Database.java:328) + at org.h2.engine.Engine.openSession(Engine.java:92) + at org.h2.engine.Engine.openSession(Engine.java:222) + at org.h2.engine.Engine.createSession(Engine.java:201) + at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:350) + at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:124) + at org.h2.Driver.connect(Driver.java:59) + at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:144) + at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:373) + at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:210) + at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:488) + at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:576) + at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:97) + at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) + at org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider.getConnection(DataSourceConnectionProvider.java:149) + at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:508) + at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:48) + at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.getJdbcEnvironmentUsingJdbcMetadata(JdbcEnvironmentInitiator.java:366) + at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.getJdbcEnvironment(JdbcEnvironmentInitiator.java:143) + at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:120) + at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:80) + at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:133) + at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:260) + at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:235) + at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:212) + at org.hibernate.boot.model.relational.Database.(Database.java:44) + at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.getDatabase(InFlightMetadataCollectorImpl.java:251) + at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.(InFlightMetadataCollectorImpl.java:203) + at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:180) + at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1388) + at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.populateSessionFactoryBuilder(EntityManagerFactoryBuilderImpl.java:1468) + at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1450) + at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:93) + at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:443) + at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:436) + at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:411) + at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:419) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1862) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1811) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:603) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:525) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:333) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:371) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:331) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:977) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:756) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:445) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:321) + at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$2(SpringBootContextLoader.java:155) + at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) + at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) + at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1465) + at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:600) + at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:155) + at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:114) + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:247) + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.lambda$loadContext$0(DefaultCacheAwareContextLoaderDelegate.java:167) + at org.springframework.test.context.cache.DefaultContextCache.put(DefaultContextCache.java:214) + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:160) + at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:128) + at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:200) + at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:139) + at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) + at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:242) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$1(ClassBasedTestDescriptor.java:423) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:428) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$0(ClassBasedTestDescriptor.java:422) + at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) + at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) + at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) + at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) + at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) + at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) + at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:422) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$0(ClassBasedTestDescriptor.java:334) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:333) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$1(ClassBasedTestDescriptor.java:322) + at java.base/java.util.Optional.orElseGet(Optional.java:364) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$0(ClassBasedTestDescriptor.java:321) + at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:27) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:127) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:126) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:70) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$0(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:110) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:42) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$2(NodeTestTask.java:180) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$1(NodeTestTask.java:166) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:138) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$0(NodeTestTask.java:164) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:163) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:116) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:42) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$2(NodeTestTask.java:180) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$1(NodeTestTask.java:166) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:138) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$0(NodeTestTask.java:164) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:163) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:116) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:36) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:52) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:58) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:246) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:218) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:179) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:66) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:157) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:65) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:125) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:58) + at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$2(InterceptingLauncher.java:57) + at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25) + at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:56) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:58) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:125) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.executeWithCancellationToken(LauncherAdapter.java:68) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.execute(LauncherAdapter.java:54) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:203) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:168) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:136) + at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385) + at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162) + at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507) + at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495) +Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /opt/projects/RITz-whatsapp-bot/db/whatsappbot.mv.db [2.4.240/7]" [50000-240] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + ... 153 more +Caused by: org.h2.mvstore.MVStoreException: The file is locked: /opt/projects/RITz-whatsapp-bot/db/whatsappbot.mv.db [2.4.240/7] + at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) + at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) + at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) + at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) + at org.h2.mvstore.MVStore.(MVStore.java:291) + ... 147 more +2026-08-20 04:52:47.925225Z database: flush +org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /opt/projects/RITz-whatsapp-bot/db/whatsappbot.mv.db [2.4.240/7]" [50000-240] + at org.h2.message.DbException.get(DbException.java:212) + at org.h2.message.DbException.convert(DbException.java:407) + at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) + at org.h2.mvstore.MVStore.handleException(MVStore.java:1675) + at org.h2.mvstore.MVStore.panic(MVStore.java:502) + at org.h2.mvstore.MVStore.(MVStore.java:296) + at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2164) + at org.h2.mvstore.db.Store.(Store.java:133) + at org.h2.engine.Database.(Database.java:328) + at org.h2.engine.Engine.openSession(Engine.java:92) + at org.h2.engine.Engine.openSession(Engine.java:222) + at org.h2.engine.Engine.createSession(Engine.java:201) + at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:350) + at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:124) + at org.h2.Driver.connect(Driver.java:59) + at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:144) + at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:373) + at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:210) + at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:488) + at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:576) + at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:97) + at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) + at org.hibernate.engine.jdbc.connections.internal.DataSourceConnectionProvider.getConnection(DataSourceConnectionProvider.java:149) + at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:508) + at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:44) + at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:37) + at org.hibernate.tool.schema.internal.exec.ImprovedExtractionContextImpl.getJdbcConnection(ImprovedExtractionContextImpl.java:61) + at org.hibernate.tool.schema.extract.spi.ExtractionContext.getQueryResults(ExtractionContext.java:41) + at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl.extractMetadata(SequenceInformationExtractorLegacyImpl.java:36) + at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.initializeSequences(DatabaseInformationImpl.java:73) + at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.(DatabaseInformationImpl.java:63) + at org.hibernate.tool.schema.extract.internal.CachingDatabaseInformationImpl.(CachingDatabaseInformationImpl.java:43) + at org.hibernate.tool.schema.internal.GroupedSchemaMigratorImpl.buildDatabaseInformation(GroupedSchemaMigratorImpl.java:112) + at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:83) + at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:269) + at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$1(SchemaManagementToolCoordinator.java:101) + at java.base/java.util.HashMap.forEach(HashMap.java:1429) + at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:100) + at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:35) + at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:33) + at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:327) + at org.hibernate.internal.SessionFactoryRegistry.instantiateSessionFactory(SessionFactoryRegistry.java:64) + at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:458) + at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1452) + at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:93) + at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:443) + at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:436) + at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:411) + at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:419) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1862) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1811) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:603) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:525) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:333) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:371) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:331) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:977) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:756) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:445) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:321) + at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$2(SpringBootContextLoader.java:155) + at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) + at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) + at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1465) + at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:600) + at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:155) + at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:114) + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:247) + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.lambda$loadContext$0(DefaultCacheAwareContextLoaderDelegate.java:167) + at org.springframework.test.context.cache.DefaultContextCache.put(DefaultContextCache.java:214) + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:160) + at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:128) + at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:200) + at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:139) + at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) + at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:242) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$1(ClassBasedTestDescriptor.java:423) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:428) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$0(ClassBasedTestDescriptor.java:422) + at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) + at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) + at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) + at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) + at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) + at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) + at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) + at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) + at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) + at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) + at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:422) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$0(ClassBasedTestDescriptor.java:334) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:333) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$1(ClassBasedTestDescriptor.java:322) + at java.base/java.util.Optional.orElseGet(Optional.java:364) + at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$0(ClassBasedTestDescriptor.java:321) + at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:27) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:127) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:126) + at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:70) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$0(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:144) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:110) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:42) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$2(NodeTestTask.java:180) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$1(NodeTestTask.java:166) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:138) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$0(NodeTestTask.java:164) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:163) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:116) + at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:42) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$2(NodeTestTask.java:180) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$1(NodeTestTask.java:166) + at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:138) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$0(NodeTestTask.java:164) + at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:163) + at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:116) + at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:36) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:52) + at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:58) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:246) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:218) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:179) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:66) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:157) + at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:65) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:125) + at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:58) + at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$2(InterceptingLauncher.java:57) + at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25) + at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:56) + at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:58) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:125) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.executeWithCancellationToken(LauncherAdapter.java:68) + at org.apache.maven.surefire.junitplatform.LauncherAdapter.execute(LauncherAdapter.java:54) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:203) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:168) + at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:136) + at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385) + at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162) + at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507) + at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495) +Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /opt/projects/RITz-whatsapp-bot/db/whatsappbot.mv.db [2.4.240/7]" [50000-240] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) + ... 157 more +Caused by: org.h2.mvstore.MVStoreException: The file is locked: /opt/projects/RITz-whatsapp-bot/db/whatsappbot.mv.db [2.4.240/7] + at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) + at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) + at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) + at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) + at org.h2.mvstore.MVStore.(MVStore.java:291) + ... 151 more diff --git a/src/main/java/com/rit/whatsapp/handler/StudentMenuHandler.java b/src/main/java/com/rit/whatsapp/handler/StudentMenuHandler.java index e505abc..1a63e24 100644 --- a/src/main/java/com/rit/whatsapp/handler/StudentMenuHandler.java +++ b/src/main/java/com/rit/whatsapp/handler/StudentMenuHandler.java @@ -81,6 +81,7 @@ public class StudentMenuHandler { weeklyBtn.put("id", "student_weekly_timetable"); weeklyBtn.put("title", "Weekly Timetable"); buttons.add(weeklyBtn); + System.out.println("FINAL TODAY TIMETABLE MESSAGE:\n" + todaySchedule); Map buttonMessage = WhatsAppMessageBuilder.buildButtonMessage(todaySchedule, buttons); messageService.sendInteractiveMessage(phoneNumber, buttonMessage); diff --git a/src/main/java/com/rit/whatsapp/util/MessageFormatter.java b/src/main/java/com/rit/whatsapp/util/MessageFormatter.java index 5f2d4bf..4e83e2f 100644 --- a/src/main/java/com/rit/whatsapp/util/MessageFormatter.java +++ b/src/main/java/com/rit/whatsapp/util/MessageFormatter.java @@ -95,74 +95,136 @@ public class MessageFormatter { return sb.toString(); } - public static String formatTimetable(String rawJson) { + private static class TimetableEntry { + String subjectName; + String subjectCode; + String staffName; + String staffCode; + + public TimetableEntry(String subjectName, String subjectCode, String staffName, String staffCode) { + this.subjectName = subjectName != null ? subjectName.trim() : ""; + this.subjectCode = subjectCode != null ? subjectCode.trim() : ""; + this.staffName = staffName != null ? staffName.trim() : ""; + this.staffCode = staffCode != null ? staffCode.trim() : ""; + } + + public boolean isValid() { + return !subjectName.isEmpty() && !subjectCode.isEmpty(); + } + + public String getUniqueKey() { + return subjectName + "||" + subjectCode; + } + } + + public static String formatTodayTimetable(String rawJson) { + String day = java.time.LocalDate.now() + .getDayOfWeek() + .getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.ENGLISH) + .toLowerCase(java.util.Locale.ROOT); + return formatTodayTimetableForDay(rawJson, day); + } + + public static String formatTodayTimetableForDay(String rawJson, String day) { if (rawJson == null) return "❌ Failed to retrieve timetable data."; try { JsonNode root = mapper.readTree(rawJson); + if (!root.has("data") || !root.get("data").isObject() || !root.get("data").has("schedule")) { + return "❌ Timetable details not available in this format."; + } + + JsonNode schedule = root.get("data").get("schedule"); + + System.out.println("TIMETABLE DEBUG"); + System.out.println("Current day: " + day.toUpperCase()); + System.out.println("Normalized day: " + day); + boolean hasSchedule = schedule.has(day); + System.out.println("Schedule found: " + hasSchedule); + StringBuilder sb = new StringBuilder(); - sb.append("\ud83d\uddd3\ufe0f *Today's Classes*\n\n"); + sb.append("📅 *Today's Classes*\n"); + sb.append("━━━━━━━━━━━━━━\n\n"); - boolean validFormat = false; - boolean hasClassesToday = false; - - if (root.has("data") && root.get("data").isObject() && root.get("data").has("schedule")) { - validFormat = true; - JsonNode schedule = root.get("data").get("schedule"); + if (hasSchedule) { + JsonNode daySchedule = schedule.get(day); - String today = java.time.LocalDate.now().getDayOfWeek().toString().toLowerCase(); + int maxPeriod = 7; + java.util.Iterator fieldNames = daySchedule.fieldNames(); + java.util.List foundPeriods = new java.util.ArrayList<>(); + while (fieldNames.hasNext()) { + String fName = fieldNames.next(); + foundPeriods.add(fName); + try { + int p = Integer.parseInt(fName); + if (p > maxPeriod) { + maxPeriod = p; + } + } catch (NumberFormatException e) {} + } + System.out.println("Periods found: [" + String.join(", ", foundPeriods) + "]"); - if (schedule.has(today)) { - JsonNode todaySchedule = schedule.get(today); + for (int period = 1; period <= maxPeriod; period++) { + String periodKey = String.valueOf(period); + sb.append(getNumberEmoji(period)).append(" "); - if (todaySchedule.isObject() && todaySchedule.size() > 0) { - java.util.Iterator> fields = todaySchedule.fields(); - while (fields.hasNext()) { - java.util.Map.Entry field = fields.next(); - String timeOrPeriod = field.getKey(); - JsonNode details = field.getValue(); - - if (details.isObject()) { - String course = details.has("course_name") ? details.get("course_name").asText() : - (details.has("name") ? details.get("name").asText() : "Unknown"); - String room = details.has("room") ? details.get("room").asText() : ""; - - sb.append("*").append(timeOrPeriod).append(":* ").append(course); - if (!room.isEmpty()) sb.append(" (Room ").append(room).append(")"); - sb.append("\n"); - } else { - sb.append("*").append(timeOrPeriod).append(":* ").append(details.asText()).append("\n"); + if (daySchedule.has(periodKey)) { + JsonNode periodNode = daySchedule.get(periodKey); + java.util.List entries = new java.util.ArrayList<>(); + + if (periodNode.isArray()) { + for (JsonNode node : periodNode) { + String sName = node.has("subjectName") ? node.get("subjectName").asText() : ""; + String sCode = node.has("subjectCode") ? node.get("subjectCode").asText() : ""; + String stName = node.has("staffName") ? node.get("staffName").asText() : ""; + String stCode = node.has("staffCode") ? node.get("staffCode").asText() : ""; + entries.add(new TimetableEntry(sName, sCode, stName, stCode)); } - hasClassesToday = true; } - } else if (todaySchedule.isArray() && todaySchedule.size() > 0) { - for (JsonNode details : todaySchedule) { - String time = details.has("time") ? details.get("time").asText() : "TBA"; - String course = details.has("course_name") ? details.get("course_name").asText() : "Unknown"; - String room = details.has("room") ? details.get("room").asText() : ""; - - sb.append("*").append(time).append(":* ").append(course); - if (!room.isEmpty()) sb.append(" (Room ").append(room).append(")"); - sb.append("\n"); - hasClassesToday = true; + + System.out.println("Period " + period + " entries: " + entries.size()); + if (!entries.isEmpty()) { + TimetableEntry first = entries.get(0); + System.out.println("Period " + period + " first subjectName: " + first.subjectName); + System.out.println("Period " + period + " first subjectCode: " + first.subjectCode); } + + java.util.Map uniqueMap = new java.util.LinkedHashMap<>(); + for (TimetableEntry entry : entries) { + if (entry.isValid()) { + uniqueMap.putIfAbsent(entry.getUniqueKey(), entry); + } + } + + if (!uniqueMap.isEmpty()) { + java.util.List formattedSubjects = new java.util.ArrayList<>(); + for (TimetableEntry entry : uniqueMap.values()) { + formattedSubjects.add("*" + entry.subjectName + "*\n `" + entry.subjectCode + "`"); + } + sb.append(String.join(" / ", formattedSubjects)).append("\n\n"); + } else { + sb.append("*Free Period*\n\n"); + } + } else { + sb.append("*Free Period*\n\n"); } } + } else { + sb.append("No classes scheduled for today.\n\n"); } - if (validFormat && !hasClassesToday) { - sb.append("No classes scheduled for today.\n"); - } else if (!validFormat) { - sb.append("Details not available in this format.\n"); - sb.append("\n\n*DEBUG RAW JSON:*\n").append(rawJson); - } - + sb.append("━━━━━━━━━━━━━━"); return sb.toString(); } catch (Exception e) { - return "⚠️ Error formatting timetable data.\nRAW JSON: " + rawJson; + System.err.println("Error formatting today's timetable: " + e.getMessage()); + return "⚠️ Error formatting timetable data."; } } + public static String formatTimetable(String rawJson) { + return formatTodayTimetable(rawJson); + } + public static String formatResults(String rawJson, int semester) { if (rawJson == null) return "❌ Failed to retrieve semester results."; @@ -444,76 +506,105 @@ public class MessageFormatter { return rows; } + private static String getNumberEmoji(int num) { + switch (num) { + case 1: return "1️⃣"; + case 2: return "2️⃣"; + case 3: return "3️⃣"; + case 4: return "4️⃣"; + case 5: return "5️⃣"; + case 6: return "6️⃣"; + case 7: return "7️⃣"; + case 8: return "8️⃣"; + case 9: return "9️⃣"; + default: return num + "️⃣"; + } + } + public static String formatWeeklyTimetable(String rawJson) { if (rawJson == null) return "❌ Failed to retrieve weekly timetable data."; try { JsonNode root = mapper.readTree(rawJson); - if (!root.has("data") || !root.get("data").isObject() || !root.get("data").has("schedule")) { return "❌ Weekly timetable details not available in the response."; } JsonNode schedule = root.get("data").get("schedule"); StringBuilder sb = new StringBuilder(); - sb.append("📅 *Weekly Class Timetable*\n\n"); - sb.append("```\n"); - sb.append(String.format("%-3s | %-11s | %s\n", "Day", "Time/Period", "Subject (Room)")); - sb.append("----+-------------+------------------------\n"); + sb.append("📚 WEEKLY TIMETABLE\n\n"); - String[] days = {"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"}; - String[] dayAbbrevs = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; + String[] days = {"monday", "tuesday", "wednesday", "thursday", "friday", "saturday"}; + String[] dayLabels = {"MON", "TUE", "WED", "THU", "FRI", "SAT"}; boolean hasAnyClasses = false; for (int i = 0; i < days.length; i++) { String day = days[i]; - String abbrev = dayAbbrevs[i]; + String label = dayLabels[i]; if (schedule.has(day)) { JsonNode daySchedule = schedule.get(day); - if (daySchedule.isObject() && daySchedule.size() > 0) { - java.util.Iterator> fields = daySchedule.fields(); - while (fields.hasNext()) { - java.util.Map.Entry field = fields.next(); - String timeOrPeriod = field.getKey(); - JsonNode details = field.getValue(); + sb.append(label).append("\n"); + + int maxPeriod = 7; + java.util.Iterator fieldNames = daySchedule.fieldNames(); + while (fieldNames.hasNext()) { + try { + int p = Integer.parseInt(fieldNames.next()); + if (p > maxPeriod) { + maxPeriod = p; + } + } catch (NumberFormatException e) {} + } + + for (int period = 1; period <= maxPeriod; period++) { + String periodKey = String.valueOf(period); + sb.append(getNumberEmoji(period)).append(" "); + + if (daySchedule.has(periodKey)) { + JsonNode details = daySchedule.get(periodKey); + java.util.Set subjects = new java.util.LinkedHashSet<>(); - String course = ""; - String room = ""; - if (details.isObject()) { - course = details.has("course_name") ? details.get("course_name").asText() : - (details.has("name") ? details.get("name").asText() : "Unknown"); - room = details.has("room") ? details.get("room").asText() : ""; - } else { - course = details.asText(); + if (details.isArray()) { + for (JsonNode entry : details) { + String name = entry.has("subjectName") ? entry.get("subjectName").asText() : ""; + String code = entry.has("subjectCode") ? entry.get("subjectCode").asText() : ""; + if (!name.trim().isEmpty()) { + subjects.add(name.trim() + " — " + code.trim()); + } + } + } else if (details.isObject()) { + String name = details.has("course_name") ? details.get("course_name").asText() : + (details.has("name") ? details.get("name").asText() : + (details.has("subjectName") ? details.get("subjectName").asText() : "")); + String code = details.has("course_code") ? details.get("course_code").asText() : + (details.has("code") ? details.get("code").asText() : + (details.has("subjectCode") ? details.get("subjectCode").asText() : "")); + if (!name.trim().isEmpty()) { + subjects.add(name.trim() + " — " + code.trim()); + } } - String subjectRoom = room.isEmpty() ? course : course + " (" + room + ")"; - sb.append(String.format("%-3s | %-11s | %s\n", abbrev, truncate(timeOrPeriod, 11), subjectRoom)); - hasAnyClasses = true; - } - } else if (daySchedule.isArray() && daySchedule.size() > 0) { - for (JsonNode details : daySchedule) { - String time = details.has("time") ? details.get("time").asText() : "TBA"; - String course = details.has("course_name") ? details.get("course_name").asText() : "Unknown"; - String room = details.has("room") ? details.get("room").asText() : ""; - - String subjectRoom = room.isEmpty() ? course : course + " (" + room + ")"; - sb.append(String.format("%-3s | %-11s | %s\n", abbrev, truncate(time, 11), subjectRoom)); - hasAnyClasses = true; + if (!subjects.isEmpty()) { + sb.append(String.join(" / ", subjects)).append("\n"); + hasAnyClasses = true; + } else { + sb.append("—\n"); + } + } else { + sb.append("—\n"); } } + sb.append("\n"); } } - sb.append("```"); - if (!hasAnyClasses) { - return "📅 *Weekly Class Timetable*\n\nNo classes scheduled for this week."; + return "📚 WEEKLY TIMETABLE\n\nNo classes scheduled for this week."; } - return sb.toString(); + return sb.toString().trim(); } catch (Exception e) { return "⚠️ Error formatting weekly timetable data."; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 5f14489..700cd92 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -19,4 +19,4 @@ spring.h2.console.enabled=true # Encryption Secret for Passcodes (Must be 16, 24, or 32 bytes) bot.encryption.secret=12345678901234567890123456789012 -whatsapp.api-token=EAAc3Wjql00oBSdoBhXozC8gKyWN9Il6cqpjvE531hZAnuFqOfHCuCBGt8hYZCICzm2dnXsH2RR3HIi3dpZAZBbNFE9Xj8Hvs3TS94nHrmxevicgThlqSQDjZCDm6SS1qFvt7NwRjiy1TWtZCuLLvcTxyjjNPUtjt91OZBr5DtddHzESLezP6R3oJEzIhSjLqlRAvNwfhW8wDYiCkkx0Qw69iO2UeodHw5E8Q86kZCPEvocldDZCyb7qZCMZAELPbYJYkWVLk8MjVyZAXjmnoGFnS5n4ZAsXBE +whatsapp.api-token=EAAc3Wjql00oBSUjYhQ1TZBiNJNmwNlpktCtJpqSLXycDVjWjPiP5dBWHvvFX2Deq1tGXQO2jgVOGChnJk9jsMxLVdxvUs4z7g7xlfcQtAPYiHwvQbPVSbGTTCBvjv2IvHf7Ql2iNBKL72GZCrxAJrQpmjHUG5sbQkVWmMpNfwgI52iDBw0BifU1QhQHFEOnQfiPu19KUp3hJOg6XkUbbXyfBKvbSkZB57xj0XHMv6wgpqDrQwAACOjSlSPrNEC3eZB9DbZAqWYnPkJCsX99Pz3GhA diff --git a/src/test/java/com/rit/whatsapp/WhatsappBotApplicationTests.java b/src/test/java/com/rit/whatsapp/WhatsappBotApplicationTests.java index 5bdaf10..2959ede 100644 --- a/src/test/java/com/rit/whatsapp/WhatsappBotApplicationTests.java +++ b/src/test/java/com/rit/whatsapp/WhatsappBotApplicationTests.java @@ -10,4 +10,46 @@ class WhatsappBotApplicationTests { void contextLoads() { } + @Test + void testFormatTodayTimetableThursday() { + String thursdayJson = "{\n" + + " \"success\": true,\n" + + " \"data\": {\n" + + " \"schedule\": {\n" + + " \"thursday\": {\n" + + " \"1\": [{\"subjectName\":\"Business Analytics\",\"subjectCode\":\"CB23531\",\"staffName\":\"FOUZIA SULTHANA K.\",\"staffCode\":\"CS84\"}],\n" + + " \"2\": [{\"subjectName\":\"Computer Networks\",\"subjectCode\":\"CS23511\",\"staffName\":\"SELVAKUMARAN S.\",\"staffCode\":\"CS91\"}],\n" + + " \"3\": [\n" + + " {\"subjectName\":\"Deep Learning\",\"subjectCode\":\"AD23511\",\"staffName\":\"DIVYA S\",\"staffCode\":\"CS193\"},\n" + + " {\"subjectName\":\"Deep Learning\",\"subjectCode\":\"AD23511\",\"staffName\":\"SARANYA S.\",\"staffCode\":\"CS85\"},\n" + + " {\"subjectName\":\"Deep Learning\",\"subjectCode\":\"AD23511\",\"staffName\":\"DR.VIDHYA M\",\"staffCode\":\"CS256\"},\n" + + " {\"subjectName\":\"Deep Learning\",\"subjectCode\":\"AD23511\",\"staffName\":\"SANDHIYARAJESHWARI K\",\"staffCode\":\"CS153\"},\n" + + " {\"subjectName\":\"Deep Learning\",\"subjectCode\":\"AD23511\",\"staffName\":\"MONIKAA R\",\"staffCode\":\"CS178\"},\n" + + " {\"subjectName\":\"Deep Learning\",\"subjectCode\":\"AD23511\",\"staffName\":\"ARTHI A.\",\"staffCode\":\"CS87\"}\n" + + " ],\n" + + " \"4\": [{\"subjectName\":\"Image and Video Analytics\",\"subjectCode\":\"AD23V15\",\"staffName\":\"JOEL J\",\"staffCode\":\"CS226\"}],\n" + + " \"5\": [{\"subjectName\":\"Big Data Analytics\",\"subjectCode\":\"AD23V12\",\"staffName\":\"KAVITHA V\",\"staffCode\":\"CS253\"}],\n" + + " \"6\": [\n" + + " {\"subjectName\":\"Data Exploration and Visualization\",\"subjectCode\":\"AD23532\",\"staffName\":\"DR.VIDHYA M\",\"staffCode\":\"CS256\"},\n" + + " {\"subjectName\":\"Data Exploration and Visualization\",\"subjectCode\":\"AD23532\",\"staffName\":\"DR.VIDHYA M\",\"staffCode\":\"CS256\"}\n" + + " ],\n" + + " \"7\": [{\"subjectName\":\"\",\"subjectCode\":\"\",\"staffName\":\"FOUZIA SULTHANA K.\",\"staffCode\":\"CS84\"}]\n" + + " }\n" + + " }\n" + + " }\n" + + "}"; + + String formatted = com.rit.whatsapp.util.MessageFormatter.formatTodayTimetableForDay(thursdayJson, "thursday"); + System.out.println("Formatted output:\n" + formatted); + + org.junit.jupiter.api.Assertions.assertTrue(formatted.contains("📅 *Today's Classes*")); + org.junit.jupiter.api.Assertions.assertTrue(formatted.contains("1️⃣ *Business Analytics*\n `CB23531`")); + org.junit.jupiter.api.Assertions.assertTrue(formatted.contains("2️⃣ *Computer Networks*\n `CS23511`")); + org.junit.jupiter.api.Assertions.assertTrue(formatted.contains("3️⃣ *Deep Learning*\n `AD23511`")); + org.junit.jupiter.api.Assertions.assertTrue(formatted.contains("4️⃣ *Image and Video Analytics*\n `AD23V15`")); + org.junit.jupiter.api.Assertions.assertTrue(formatted.contains("5️⃣ *Big Data Analytics*\n `AD23V12`")); + org.junit.jupiter.api.Assertions.assertTrue(formatted.contains("6️⃣ *Data Exploration and Visualization*\n `AD23532`")); + org.junit.jupiter.api.Assertions.assertTrue(formatted.contains("7️⃣ *Free Period*")); + } + }