+- Az extra notes hogyan értelmezendő?
+- Egy video lehet e többször hozzáadva gyan ahhoz a listához? Ha nem ,kéne csekkolni betöltésnél...
+
actors_talent, Actors/Talent, , string, null required: false
director, Director, , string, null required: false
episode, Episode, ,
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>mc-vod-sync-app</artifactId>
- <version>0.0.1-SNAPSHOT</version>
+ <version>0.0.3</version>
<parent>
<groupId>hu.user</groupId>
<artifactId>mc-vod-sync</artifactId>
username: db2admin
password: password
mail:
- host: mx.in.useribm.hu
+# host: mx.in.useribm.hu
# port: 587
# spring.mail.username=<login user to smtp server>
# spring.mail.password=<login password to smtp server>
# spring.mail.properties.mail.smtp.auth=true
# spring.mail.properties.mail.smtp.starttls.enable=true
logging:
+ config:
level:
org.hibernate.engine.jdbc.spi.SqlExceptionHelper: ERROR
# org.springframework.web.client.RestTemplate: DEBUG
# httpclient.wire: DEBUG
mc-vod-sync:
service:
- target-video-insert-enabled: true
- target-playlist-insert-enabled: true
+ target-video-insert-enabled: false
+ target-playlist-insert-enabled: false
scheduler:
import-enabled: false
export-enabled: false
- sunrise-checker-enabled: false
+ sunset-checker-enabled: false
import-cron: 0 0 4 1/1 * ? *
export-cron: 0 0 4 1/1 * ? *
- sunrise-checker-cron: 0 0 4 1/1 * ? *
+ sunset-checker-cron: 0 0 4 1/1 * ? *
+ report:
+ recipient: vasary@elgekko.net
+ sender: noreply@mc-vod-sync
application:
user-name: user
password: password
+ admin: true
api:
+ base-url: https://cms.api.brightcove.com
token-request-url: https://oauth.brightcove.com/v4/access_token
client-id: 0337034f-bde3-44d4-81ba-6a4566ca8ba0
client-secret: 03Az2W6BSj4sTPSU7SVnRcJ6NDmeaFmB7EPbmuZFnt5slaC2bbscBOMvKR03CGVMxaVXUo9ao6e_gLvxUCjtgg
server:
- port: 8080
+ port: 80
servlet:
context-path: /
zk:
zul-view-resolver-enabled: true
spring:
jpa:
- # hibernate:
- # use-new-id-generator-mappings: true
- show-sql: true
+ show-sql: false
properties:
hibernate:
format_sql: true
enabled: always
datasource:
type: com.zaxxer.hikari.HikariDataSource
- url: jdbc:db2://dvdev.in.useribm.hu:50000/vodsync
+ url: jdbc:db2://localhost:25000/vodsync
+ retrieveMessagesFromServerOnGetMessage: true
username: db2admin
- password: password
-camunda.bpm:
- generic-properties.properties:
- telemetry-reporter-activate: false
- job-executor-acquire-by-priority: true
- job-execution:
- core-pool-size: 10
- #lock-time-in-millis: 600000
- database:
- type: db2
- schema-update: false
- table-prefix: CAMUNDA.
- schema-name: CAMUNDA
- webapp:
- enabled: true
- index-redirect-enabled: false
- admin-user:
- id: kermit
- password: password
- firstName: Kermit
- filter:
- create: All tasks
- job-execution.enabled: true
+ password: Password01
+ mail:
+ host: mx.in.useribm.hu
+# port: 587
+# username=<login user to smtp server>
+# password=<login password to smtp server>
+# properties.mail.smtp.auth=true
+# properties.mail.smtp.starttls.enable=true
logging:
- # config: classpath:log4j2.xml
- level:
- org.hibernate.engine.jdbc.spi.SqlExceptionHelper: ERROR
-# org.springframework.security: DEBUG
-# org.springframework.security.web: INFO
-# pattern:
-# console: "%d %-5level %logger : %msg%n"
-# file: "%d %-5level [%thread] %logger : %msg%n"
\ No newline at end of file
+ config: logback-prod.xml
+mc-vod-sync:
+ service:
+ target-video-insert-enabled: false
+ target-playlist-insert-enabled: false
+ scheduler:
+ import-enabled: false
+ export-enabled: false
+ sunset-checker-enabled: false
+ import-cron: 0 0 4 1/1 * ? *
+ export-cron: 0 0 4 1/1 * ? *
+ sunset-checker-cron: 0 0 4 1/1 * ? *
+ report:
+ recipient: vasary@elgekko.net
+ sender: noreply@mc-vod-sync
+ application:
+ user-name: user
+ password: password
+ admin: true
+ api:
+ base-url: https://cms.api.brightcove.com
+ token-request-url: https://oauth.brightcove.com/v4/access_token
+ client-id: 0337034f-bde3-44d4-81ba-6a4566ca8ba0
+ client-secret: 03Az2W6BSj4sTPSU7SVnRcJ6NDmeaFmB7EPbmuZFnt5slaC2bbscBOMvKR03CGVMxaVXUo9ao6e_gLvxUCjtgg
+ account-id: 6302605131001
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+ <property name="LOGNAME" value="mc-vod-sync"/>
+ <property name="LOGS" value="./logs"/>
+
+ <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
+ <layout class="ch.qos.logback.classic.PatternLayout">
+ <Pattern>
+ %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable
+ </Pattern>
+ </layout>
+ </appender>
+
+ <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${LOGS}/${LOGNAME}.log</file>
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
+ </encoder>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>${LOGS}/archived/${LOGNAME}-%d{yyyy-MM-dd}.log.zip</fileNamePattern>
+ </rollingPolicy>
+ </appender>
+
+ <springProfile name="prod">
+ <root level="INFO">
+ <appender-ref ref="Console"/>
+ </root>
+ <root level="INFO">
+ <appender-ref ref="RollingFile"/>
+ </root>
+ </springProfile>
+
+</configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
- <include resource="org/springframework/boot/logging/logback/base.xml"/>
+ <!-- <include resource="org/springframework/boot/logging/logback/base.xml"/>-->
<!-- <property name="LOGS" value="./logs"/>-->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
- %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable
+ %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): [%F:%L] %msg%n %throwable
</Pattern>
</layout>
</appender>
<!-- </appender>-->
<springProfile name="dev">
- <logger name="org.springframework" level="INFO" additivity="false">
- <appender-ref ref="CONSOLE"/>
- </logger>
+ <!-- <logger name="hu.user.mcvodsync" level="INFO" additivity="false">-->
+ <!-- <appender-ref ref="Console"/>-->
+ <!-- </logger>-->
+ <!-- <logger name="org.springframework" level="INFO" additivity="false">-->
+ <!-- <appender-ref ref="Console"/>-->
+ <!-- </logger>-->
<root level="INFO">
- <appender-ref ref="CONSOLE"/>
+ <appender-ref ref="Console"/>
</root>
</springProfile>
--- /dev/null
+/*
+ * Copyright (c) $today.year-$today.month-24.
+ * By elGekko
+ */
+
+package hu.user.mcvodsync;
+
+import lombok.extern.log4j.Log4j2;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+
+@Log4j2
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@ActiveProfiles("dev")
+@TestPropertySource("classpath:application-dev.yaml")
+public class AssetExportIT extends AssetImportExportBase {
+
+ @Test
+ public void initialLoadTest() throws Exception {
+ cleanupLocal();
+ importFile("src/test/resources/testdata.xlsx");
+ cleanupRemote();
+ exportData();
+ assertSynchronized();
+ }
+
+ @Test
+ public void changeVideoTest() throws Exception {
+ cleanupLocal();
+ importFile("src/test/resources/testdata.xlsx");
+ importFile("src/test/resources/testdata-change-video.xlsx");
+ cleanupRemote();
+ exportData();
+ assertSynchronized();
+ }
+
+ @Test
+ public void deleteVideoTest() throws Exception {
+ cleanupLocal();
+ importFile("src/test/resources/testdata.xlsx");
+ importFile("src/test/resources/testdata-delete-video.xlsx");
+ cleanupRemote();
+ exportData();
+ assertSynchronized();
+ }
+
+ @Test
+ public void changePlaylistOrderTest() throws Exception {
+ cleanupLocal();
+ importFile("src/test/resources/testdata.xlsx");
+ importFile("src/test/resources/testdata-change-playlist-order.xlsx");
+ cleanupRemote();
+ exportData();
+ assertSynchronized();
+ }
+
+ @Test
+ public void duplicateVideoTest() throws Exception {
+ cleanupLocal();
+ importFile("src/test/resources/testdata.xlsx");
+ importFile("src/test/resources/testdata-duplicate-video.xlsx");
+ cleanupRemote();
+ exportData();
+ assertSynchronized();
+ }
+
+
+}
import hu.user.mcvodsync.db.repository.AssetRepository;
import hu.user.mcvodsync.db.repository.AssetSyncRepository;
import hu.user.mcvodsync.db.repository.PlaylistSyncRepository;
+import hu.user.mcvodsync.service.data.CompositeSummary;
+import hu.user.mcvodsync.service.data.Summary;
import hu.user.mcvodsync.service.in.AssetImportService;
-import hu.user.mcvodsync.service.in.ImportSummary;
import hu.user.mcvodsync.service.in.VodXlsProcessor;
-import hu.user.mcvodsync.service.out.AssetExportService;
+import hu.user.mcvodsync.service.out.SyncProcessor;
import hu.user.mcvodsync.service.out.VideoMapper;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
private AssetImportService assetImportService;
@Autowired
- private AssetExportService assetExportService;
+ private SyncProcessor syncProcessor;
@Autowired
private BrightCoveClient brightCoveClient;
protected String getPlaylistSyncIds(String name) {
List<PlaylistSync> playlists = playlistSyncRepository.findAllByName(name);
assertEquals(1, playlists.size());
- return StringUtils.join(playlists.get(0).getIds(), ",");
+ List<String> catalogIds = playlists.get(0).getIds().stream().map(assetRepository::findCatalogIdById).collect(Collectors.toList());
+ return StringUtils.join(catalogIds, ",");
}
- protected void cleanup() {
+ protected void cleanup() throws Exception {
cleanupLocal();
cleanupRemote();
assetRepository.deleteAllInBatch();
}
- protected void cleanupRemote() {
+ protected void cleanupRemote() throws InterruptedException {
log.info("cleanupRemote");
cleanupRemotePlaylists();
+ waitForPlaylistCleanupCompleted();
+ Thread.sleep(3000);
cleanupRemoteVideos();
+ waitForVideoCleanupCompleted();
log.info("cleanupRemote OK");
}
+ private void waitForVideoCleanupCompleted() {
+ while (true) {
+ int count = 0;
+ PagedSearch<Video> page = PagedSearch.<Video>builder()
+ .query("tags:mc-vod-sync")
+ .build();
+ while (brightCoveClient.getVideosPaged(page)) {
+ count += page.getData().size();
+ }
+ if (count == 0) {
+ break;
+ }
+ }
+ }
+
private void cleanupRemoteVideos() {
PagedSearch<Video> page = PagedSearch.<Video>builder()
.query("tags:mc-vod-sync")
}
}
+ private void waitForPlaylistCleanupCompleted() {
+ while (true) {
+ int count = 0;
+ PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
+ .query("description:mc-vod-sync")
+ .sort("name")
+ .build();
+ while (brightCoveClient.getPlaylistsPaged(page)) {
+ count += page.getData().size();
+ }
+ if (count == 0) {
+ break;
+ }
+ }
+ }
+
private void cleanupRemotePlaylists() {
PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
.query("description:mc-vod-sync")
protected void cleanupLocal() {
log.info("cleanupLocal");
+ assetRepository.deleteAllInBatch();
+ cleanupLocalSync();
+ log.info("cleanupLocal OK");
+ }
+
+ protected void cleanupLocalSync() {
+ log.info("cleanupLocalSync");
playlistSyncRepository.deleteAllInBatch();
assetSyncRepository.deleteAllInBatch();
- log.info("cleanupLocal OK");
+ log.info("cleanupLocalSync OK");
}
- protected ImportSummary importFile(String xlsFile) {
- ImportSummary summary;
+ protected CompositeSummary importFile(String xlsFile) {
+ CompositeSummary summary;
try {
Path input = Paths.get(xlsFile);
summary = vodXlsProcessor.process(input.getFileName().toString(), Files.readAllBytes(input));
}
protected void exportData() {
- assetExportService.export();
+ syncProcessor.export();
}
protected void assertSynchronized() {
List<Asset> assets = assetRepository.findAll();
assets.forEach(this::assertVideoSynchronized);
- Map<String, List<String>> playLists = assetImportService.getPlayLists();
+ Map<String, List<Long>> playLists = assetImportService.getPlayLists();
playLists.forEach(this::assertPlaylistSynchronized);
}
assertEquals(asset.getProductionYear(), remoteAsset.getProductionYear());
}
- protected void assertPlaylistSynchronized(String playlistName, List<String> ids) {
+ protected void assertPlaylistSynchronized(String playlistName, List<Long> ids) {
Playlist playlist = null;
try {
playlist = brightCoveClient.getPlaylistsByName(playlistName);
}
assertNotNull(playlist);
assertNotNull(playlist.getVideoIds());
- List<String> remoteIds = playlist.getVideoIds().stream().map(assetRepository::findCatalogIdByReferenceId).collect(Collectors.toList());
- assertEquals(ids, remoteIds);
+ List<String> remoteIds = playlist.getVideoIds().stream().map(id -> assetRepository.findIdByReferenceId(id, playlistName)).collect(Collectors.toList());
+ String actual = StringUtils.join(remoteIds, ",");
+ String expected = StringUtils.join(ids.stream().map(String::valueOf).collect(Collectors.toList()), ",");
+ assertEquals(expected, actual);
}
- protected void checkSummary(ImportSummary summary, long expectedAll, long expectedInsert, long expectedUpdate, long expectedDelete) {
+ protected void checkSummary(Summary summary, long expectedAll, long expectedInsert, long expectedUpdate, long expectedDelete) {
assertEquals(expectedAll, summary.getAll());
assertEquals(expectedAll, summary.getSuccess());
- assertEquals(expectedInsert, summary.getInserted());
- assertEquals(expectedUpdate, summary.getUpdated());
- assertEquals(expectedDelete, summary.getDeleted());
+ assertEquals(expectedInsert, summary.getInsert());
+ assertEquals(expectedUpdate, summary.getUpdate());
+ assertEquals(expectedDelete, summary.getDelete());
}
protected void checkAsset(long expected, SyncType syncType, long expectedAllCount) {
package hu.user.mcvodsync;
import hu.user.mcvodsync.db.SyncType;
-import hu.user.mcvodsync.service.in.ImportSummary;
+import hu.user.mcvodsync.service.data.CompositeSummary;
import lombok.extern.log4j.Log4j2;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@SpringBootTest
@ActiveProfiles("dev")
@TestPropertySource("classpath:application-dev.yaml")
-public class AssetImportExportIT extends AssetImportExportBase {
+public class AssetImportIT extends AssetImportExportBase {
@BeforeClass
public static void beforeClass() {
public void init() {
}
+ // @Test
+ public void initialBigLoadTest() throws IOException {
+ log.info("initialBigLoadTest");
+ cleanupLocal();
+ CompositeSummary summary = importFile("src/test/resources/testdata-big.xlsx");
+// checkSummary(summary.getVideo(), 9, 9, 0, 0);
+// checkAsset(9, SyncType.INSERT, 9);
+// checkPlaylist(3, SyncType.INSERT, 3);
+// assertEquals("CCEM110180,CCEM110183,CCEM110068", getPlaylistSyncIds("Playlist1"));
+// assertEquals("CCEM128963,CCEM128965,CCEM128967", getPlaylistSyncIds("Playlist2"));
+// assertEquals("CCEF008678,CCEM001148,CCEM072086", getPlaylistSyncIds("Playlist3"));
+ }
+
@Test
public void initialLoadTest() throws IOException {
// Path xlsFile = Paths.get("src/test/resources/testdata-big.xlsx");
log.info("initialLoadTest");
- cleanup();
- ImportSummary summary = importFile("src/test/resources/testdata.xlsx");
- checkSummary(summary, 9, 9, 0, 0);
+ cleanupLocal();
+ CompositeSummary summary = importFile("src/test/resources/testdata.xlsx");
+ checkSummary(summary.getVideo(), 9, 9, 0, 0);
checkAsset(9, SyncType.INSERT, 9);
checkPlaylist(3, SyncType.INSERT, 3);
assertEquals("CCEM110180,CCEM110183,CCEM110068", getPlaylistSyncIds("Playlist1"));
assertEquals("CCEM128963,CCEM128965,CCEM128967", getPlaylistSyncIds("Playlist2"));
assertEquals("CCEF008678,CCEM001148,CCEM072086", getPlaylistSyncIds("Playlist3"));
-
- exportData();
- assertSynchronized();
}
@Test
public void changeVideoTest() throws IOException {
log.info("changeVideoTest");
- ImportSummary summary = importFile("src/test/resources/testdata.xlsx");
cleanupLocal();
+ CompositeSummary summary = importFile("src/test/resources/testdata.xlsx");
+ cleanupLocalSync();
summary = importFile("src/test/resources/testdata-change-video.xlsx");
- checkSummary(summary, 9, 0, 1, 0);
+ checkSummary(summary.getVideo(), 9, 0, 1, 0);
checkAsset(1, SyncType.UPDATE, 1);
checkPlaylist(0, null, 0);
}
@Test
public void deleteVideoTest() {
log.info("deleteVideoTest");
- ImportSummary summary = importFile("src/test/resources/testdata.xlsx");
cleanupLocal();
+ CompositeSummary summary = importFile("src/test/resources/testdata.xlsx");
+ cleanupLocalSync();
summary = importFile("src/test/resources/testdata-delete-video.xlsx");
- checkSummary(summary, 9, 0, 1, 1);
- checkAsset(1, SyncType.UPDATE, 2);
- checkAsset(1, SyncType.DELETE, 2);
+ checkSummary(summary.getVideo(), 9, 0, 0, 1);
+ checkAsset(1, SyncType.DELETE, 1);
checkPlaylist(1, SyncType.UPDATE, 1);
assertEquals("CCEM110180,CCEM110183", getPlaylistSyncIds("Playlist1"));
}
@Test
public void changePlaylistOrderTest() {
log.info("changePlaylistOrderTest");
- ImportSummary summary = importFile("src/test/resources/testdata.xlsx");
cleanupLocal();
+ CompositeSummary summary = importFile("src/test/resources/testdata.xlsx");
+ cleanupLocalSync();
summary = importFile("src/test/resources/testdata-change-playlist-order.xlsx");
- checkSummary(summary, 8, 0, 1, 0);
+ checkSummary(summary.getVideo(), 8, 0, 1, 0);
checkAsset(1, SyncType.UPDATE, 1);
checkPlaylist(1, SyncType.UPDATE, 1);
assertEquals("CCEM001148,CCEM072086,CCEF008678", getPlaylistSyncIds("Playlist3"));
}
+ @Test
+ public void duplicateVideoTest() throws IOException {
+ log.info("changeVideoTest");
+ cleanupLocal();
+ CompositeSummary summary = importFile("src/test/resources/testdata.xlsx");
+ cleanupLocalSync();
+ summary = importFile("src/test/resources/testdata-duplicate-video.xlsx");
+ checkSummary(summary.getVideo(), 10, 1, 0, 0);
+ checkAsset(1, SyncType.INSERT, 1);
+ checkPlaylist(1, SyncType.INSERT, 1);
+ assertEquals("CCEF008678", getPlaylistSyncIds("Playlist4"));
+ }
+
}
assertEquals(asset.getCatalogId(), video.getReferenceId());
assertEquals(asset.getProgLocalTitle(), video.getName());
- assertEquals(asset.getHubInfo(), video.getCustomFields().get(VideoMapper.NOTES));
+ assertEquals(asset.getHubInfo(), video.getCustomFields().get(VideoMapper.CUSTOM_NOTES));
}
@Test
Video video = new Video();
video.setReferenceId("setReferenceId");
video.setName("name");
- video.putCustomFieldsItem(VideoMapper.NOTES, "notes");
+ video.putCustomFieldsItem(VideoMapper.CUSTOM_NOTES, "notes");
Asset asset = videoMapper.toAsset(video);
assertEquals(video.getReferenceId(), asset.getCatalogId());
assertEquals(video.getName(), asset.getProgLocalTitle());
- assertEquals(video.getCustomFields().get(VideoMapper.NOTES), asset.getHubInfo());
+ assertEquals(video.getCustomFields().get(VideoMapper.CUSTOM_NOTES), asset.getHubInfo());
}
+
}
@Autowired
private BrightCoveClient bcClient;
+ @Test
+ public void testGetPlaylistById() {
+ Playlist playList = bcClient.getPlaylistsById("1760978144285528866");
+ logPlaylist(playList);
+ }
+
private List<String> getVideos() {
List<String> ids = new ArrayList<>();
@Test
public void testGetPlayListsByName() {
PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
- .query("name:Playlist 0000")
+ .query("name:Playlist1")
.sort("name")
.build();
Video video = new Video();
video.setName("TEST video 00001");
video.setReferenceId("MCVODSYNC10001");
- video.setTags(Collections.singletonList("mc-vod-sync"));
+ video.addTagsItem("tag1");
+ video.addTagsItem("mc-vod-sync");
video.setCustomFields(ImmutableMap.of("Notes", "notes"));
Video createdVideo = bcClient.createVideo(video);
logVideo(createdVideo);
logVideo(createdVideo);
}
+ @Test
+ public void testCreateAndUpdateVideo() {
+ String refId = "MCVODSYNC00012";
+ Video video = bcClient.getVideoByReferenceId(refId);
+ if (Objects.isNull(video)) {
+ video.setName("TEST video 0000 for update");
+ video.setReferenceId(refId);
+ video.setTags(Collections.singletonList("mc-vod-sync"));
+ video = bcClient.createVideo(video);
+ }
+ video.setName("TEST video 0000 for update " + System.currentTimeMillis());
+ video = bcClient.updateVideo(video.getId(), video);
+ logVideo(video);
+ }
+
+ @Test
+ public void testCheckVideos() {
+ Video video = bcClient.getVideoByReferenceId("CCEM110068");
+ logVideo(video);
+ video = bcClient.getVideoByReferenceId("CCEM110180");
+ logVideo(video);
+ video = bcClient.getVideoByReferenceId("CCEM110183");
+ logVideo(video);
+ video = bcClient.getVideoByReferenceId("CCEM110185");
+ logVideo(video);
+ }
+
@Test
public void testGetCustomFields() {
VideoFields videoFields = bcClient.getVideoCustomFields();
package hu.user.mcvodsync;
+import hu.user.mcvodsync.service.data.CompositeSummary;
import hu.user.mcvodsync.service.mail.EmailSendService;
-import org.junit.jupiter.api.Test;
+import lombok.extern.log4j.Log4j2;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
+import java.time.Duration;
+import java.time.Instant;
+
+@Log4j2
@RunWith(SpringRunner.class)
+@ComponentScan("hu.user.mcvodsync")
@SpringBootTest
@ActiveProfiles("dev")
@TestPropertySource("classpath:application-dev.yaml")
-public class EmailSendServiceTest {
+public class EmailSendServiceIT {
@Autowired
EmailSendService emailSendService;
@Test
public void sendEmail() {
- emailSendService.sendSimpleMessage();
+ CompositeSummary summary = CompositeSummary.builder().build();
+ summary.setStarted(Instant.now());
+ summary.setFinished(Instant.now().plus(Duration.ofHours(1)));
+ emailSendService.sendSimpleMessage("BrightCove export report", summary);
+ log.info("Test email sent");
}
+
}
package hu.user.mcvodsync;
-import hu.user.mcvodsync.service.schedule.ScheduledSunriseChacker;
+import hu.user.mcvodsync.service.schedule.ScheduledSunsetChacker;
import lombok.extern.log4j.Log4j2;
import org.junit.Test;
import org.junit.runner.RunWith;
private TaskScheduler taskScheduler;
@Autowired
- private ScheduledSunriseChacker scheduledSunriseChacker;
+ private ScheduledSunsetChacker scheduledSunsetChacker;
@Test
public void testSemaphore() throws InterruptedException {
log.info("Test start");
Thread.sleep(1000);
- taskScheduler.schedule(scheduledSunriseChacker, Instant.now());
+ taskScheduler.schedule(scheduledSunsetChacker, Instant.now());
Thread.sleep(5000);
log.info("Test finish");
}
package hu.user.mcvodsync;
import com.google.common.collect.Lists;
+import hu.user.mcvodsync.db.Asset;
import hu.user.mcvodsync.db.AssetSync;
import hu.user.mcvodsync.db.PlaylistSync;
import hu.user.mcvodsync.db.SyncType;
import hu.user.mcvodsync.db.repository.AssetRepository;
import hu.user.mcvodsync.db.repository.AssetSyncRepository;
import hu.user.mcvodsync.db.repository.PlaylistSyncRepository;
+import hu.user.mcvodsync.service.data.CompositeSummary;
import hu.user.mcvodsync.service.in.AssetImportService;
-import hu.user.mcvodsync.service.in.ImportSummary;
import lombok.extern.log4j.Log4j2;
import org.junit.Test;
import org.junit.runner.RunWith;
List<String> playlists = assetRepository.queryDistinctPlaylists();
assertNotNull(playlists);
- List<String> assets = assetRepository.queryPlaylistsAssets(playlists.get(0));
+ List<Long> assets = assetRepository.queryPlaylistsAssets(playlists.get(0));
assertNotNull(assets);
}
@Test
public void queryExpired() {
LocalDate currentDate = LocalDate.parse("2000-01-02", DateTimeFormatter.ISO_LOCAL_DATE);
- List<String> ids = assetRepository.queryExpired(Date.valueOf(currentDate));
+ List<Asset> ids = assetRepository.queryExpired(Date.valueOf(currentDate));
assertNotNull(ids);
assertEquals(1, ids.size());
- ImportSummary summary = ImportSummary.builder().build();
- assetImportService.removeExpired(currentDate);
+ CompositeSummary summary = CompositeSummary.builder().build();
+ assetImportService.removeExpired(currentDate, summary);
ids = assetRepository.queryExpired(Date.valueOf(currentDate));
assertNotNull(ids);
PlaylistSync sync = PlaylistSync.builder()
.name("Playlist1")
.syncType(SyncType.INSERT)
- .ids(Arrays.asList("AAA", "BBB", "CCC"))
+ .ids(Arrays.asList(1L, 2L, 3L))
.build();
playlistSyncRepository.saveAndFlush(sync);
playlistSyncRepository.deleteById(sync.getId());
@ConfigurationProperties(prefix = "mc-vod-sync.api")
public class ApiProperties {
+ private String baseUrl;
+
private String tokenRequestUrl;
private String clientId;
import com.brightcove.cms.client.api.PlaylistsApi;
import com.brightcove.cms.client.api.VideosApi;
import com.brightcove.cms.client.model.*;
+import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestClientException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
@Log4j2
@Service
public class BrightCoveClient {
+ private final ApiProperties apiProperties;
private String authToken;
private long tokenExpires;
public static final String BEARER = "Bearer ";
- private final ApiClient apiClient = new ExtendedApiClient();
+ private final ApiClient extendedApiClient = new ExtendedApiClient();
private final PlaylistsApi playListApi = new PlaylistsApi();
private final VideosApi videosApi = new VideosApi();
private final CustomFieldsApi customFieldsApi = new CustomFieldsApi();
+
@Autowired
- private ApiProperties apiProperties;
+ private ObjectMapper objectMapper;
- public BrightCoveClient() {
- playListApi.setApiClient(apiClient);
- videosApi.setApiClient(apiClient);
- customFieldsApi.setApiClient(apiClient);
+ @Autowired
+ public BrightCoveClient(ApiProperties apiProperties) {
+ this.apiProperties = apiProperties;
+ extendedApiClient.setBasePath(apiProperties.getBaseUrl());
+ playListApi.setApiClient(extendedApiClient);
+ videosApi.setApiClient(extendedApiClient);
+ customFieldsApi.setApiClient(extendedApiClient);
}
private String createToken() {
final MultiValueMap<String, String> cookieParams = new LinkedMultiValueMap<String, String>();
final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();
- queryParams.putAll(apiClient.parameterToMultiValueMap(null, "q", query));
+ queryParams.putAll(extendedApiClient.parameterToMultiValueMap(null, "q", query));
if (contentType != null)
- headerParams.add("Content-Type", apiClient.parameterToString(contentType));
+ headerParams.add("Content-Type", extendedApiClient.parameterToString(contentType));
if (authorization != null)
- headerParams.add("Authorization", apiClient.parameterToString(authorization));
+ headerParams.add("Authorization", extendedApiClient.parameterToString(authorization));
final String[] localVarAccepts = {
"application/json"
};
- final List<MediaType> localVarAccept = apiClient.selectHeaderAccept(localVarAccepts);
+ final List<MediaType> localVarAccept = extendedApiClient.selectHeaderAccept(localVarAccepts);
final String[] contentTypes = {};
- final MediaType localVarContentType = apiClient.selectHeaderContentType(contentTypes);
+ final MediaType localVarContentType = extendedApiClient.selectHeaderContentType(contentTypes);
String[] authNames = new String[]{"BC_OAuth2"};
ParameterizedTypeReference<PlaylistCount> returnType = new ParameterizedTypeReference<PlaylistCount>() {
};
- return apiClient.invokeAPI("/v1/accounts/{account_id}/counts/playlists", HttpMethod.GET, uriVariables, queryParams, postBody, headerParams, cookieParams, formParams, localVarAccept, localVarContentType, authNames, returnType);
+ return extendedApiClient.invokeAPI("/v1/accounts/{account_id}/counts/playlists", HttpMethod.GET, uriVariables, queryParams, postBody, headerParams, cookieParams, formParams, localVarAccept, localVarContentType, authNames, returnType);
}
+ public Playlist getPlaylistsById(String id) {
+ return playListApi.getPlaylistById(apiProperties.getAccountId(), id, MediaType.APPLICATION_JSON_VALUE, getToken());
+ }
+
public Playlist getPlaylistsByName(String name) throws Exception {
Playlist result = null;
PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
return videosApi.createVideo(apiProperties.getAccountId(), MediaType.APPLICATION_JSON_VALUE, getToken(), fields);
}
+ List<String> updatableVideoFields = Arrays.asList("name", "tags", "long_description", "description", "custom_fields");
+
public Video updateVideo(String videoId, Video video) {
- return videosApi.updateVideo(apiProperties.getAccountId(), videoId, MediaType.APPLICATION_JSON_VALUE, getToken(), video);
+ final Map<String, Object> videoEx = objectMapper.convertValue(video, objectMapper.getTypeFactory().constructMapType(Map.class, String.class, Object.class));
+ Map<String, Object> filteredVideo = new HashMap<>();
+ updatableVideoFields.forEach(f -> filteredVideo.put(f, videoEx.get(f)));
+ updateVideoWithHttpInfo(apiProperties.getAccountId(), videoId, MediaType.APPLICATION_JSON_VALUE, getToken(), filteredVideo);
+ return getVideoById(videoId);
}
public void deleteVideo(String id) {
videosApi.deleteVideo(apiProperties.getAccountId(), "/" + id, MediaType.APPLICATION_JSON_VALUE, getToken());
}
+ //generated function expects Video object, which has prohibited for update fields like variants
+ public ResponseEntity<Map<String, Object>> updateVideoWithHttpInfo(String accountId, String videoId, String contentType, String authorization, Map<String, Object> video) throws RestClientException {
+ Object postBody = video;
+
+ // verify the required parameter 'accountId' is set
+ if (accountId == null) {
+ throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'accountId' when calling updateVideo");
+ }
+
+ // verify the required parameter 'videoId' is set
+ if (videoId == null) {
+ throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'videoId' when calling updateVideo");
+ }
+
+ // verify the required parameter 'contentType' is set
+ if (contentType == null) {
+ throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'contentType' when calling updateVideo");
+ }
+
+ // verify the required parameter 'authorization' is set
+ if (authorization == null) {
+ throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'authorization' when calling updateVideo");
+ }
+
+ // verify the required parameter 'video' is set
+ if (video == null) {
+ throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'video' when calling updateVideo");
+ }
+
+ // create path and map variables
+ final Map<String, Object> uriVariables = new HashMap<String, Object>();
+ uriVariables.put("account_id", accountId);
+ uriVariables.put("video_id", videoId);
+
+ final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
+ final HttpHeaders headerParams = new HttpHeaders();
+ final MultiValueMap<String, String> cookieParams = new LinkedMultiValueMap<String, String>();
+ final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();
+
+ if (contentType != null)
+ headerParams.add("Content-Type", extendedApiClient.parameterToString(contentType));
+ if (authorization != null)
+ headerParams.add("Authorization", extendedApiClient.parameterToString(authorization));
+
+ final String[] localVarAccepts = {
+ "application/json"
+ };
+ final List<MediaType> localVarAccept = extendedApiClient.selectHeaderAccept(localVarAccepts);
+ final String[] contentTypes = {
+ "application/json"
+ };
+ final MediaType localVarContentType = extendedApiClient.selectHeaderContentType(contentTypes);
+
+ String[] authNames = new String[]{"BC_OAuth2"};
+
+ ParameterizedTypeReference<Map<String, Object>> returnType = new ParameterizedTypeReference<Map<String, Object>>() {
+ };
+ return extendedApiClient.invokeAPI("/v1/accounts/{account_id}/videos/{video_id}", HttpMethod.PATCH, uriVariables, queryParams, postBody, headerParams, cookieParams, formParams, localVarAccept, localVarContentType, authNames, returnType);
+ }
+
+
//generated VideosApi returns Video entity not a List
private ResponseEntity<List<Video>> getVideoByIdOrReferenceIdWithHttpInfo(String accountId, String videoIds, String contentType, String authorization, Boolean includeVariants) throws RestClientException {
Object postBody = null;
-- // Bootstrap.sql
-- DROP DATABASE IF EXISTS VODSYNC;
--- CREATE DATABASE VODSYNC AUTOMATIC STORAGE YES USING CODESET UTF-8 TERRITORY hu COLLATE USING UCA500R1_S2 PAGESIZE 32 K;
-
-TRUNCATE TABLE asset IMMEDIATE;
-TRUNCATE TABLE asset_sync IMMEDIATE;
-TRUNCATE TABLE playlist_sync IMMEDIATE;
+-- CREATE DATABASE VODSYNC USING CODESET UTF-8 TERRITORY hu catalog tablespace managed by system using ('catalog_TS') user tablespace managed by system using ('data_TS') temporary tablespace managed by system using ('temporary_TS');
);
CREATE TABLE asset (
+ id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
catalog_id VARCHAR(20) NOT NULL,
reference_id VARCHAR(20),
season_id BIGINT NOT NULL,
series_id BIGINT NOT NULL,
sunset DATE NOT NULL,
sunrise DATE NOT NULL,
- playlist VARCHAR(100) NOT NULL,
- hub_info VARCHAR(100),
- prog_local_title VARCHAR(255) NOT NULL,
- season_local_title VARCHAR(255),
- series_local_title VARCHAR(255),
+ playlist VARCHAR(255) NOT NULL,
+ hub_info VARCHAR(128),
+ prog_local_title VARCHAR(250) NOT NULL,
+ prog_synopsis_short_hun VARCHAR(250),
+ prog_synopsis_full_hun VARCHAR(5000),
+ season_local_title VARCHAR(128),
+ series_local_title VARCHAR(128),
season_nr INTEGER,
episode_nr INTEGER,
production_year INTEGER,
content_type VARCHAR(50),
content_source VARCHAR(50),
age_rating VARCHAR(10),
- CONSTRAINT pk_catalog_id PRIMARY KEY (catalog_id)
+ director VARCHAR(128),
+ x_cast VARCHAR(128),
+ prog_genre1 VARCHAR(128),
+ prog_genre2 VARCHAR(128),
+ prog_genre3 VARCHAR(128),
+ CONSTRAINT pk_asset PRIMARY KEY (id)
);
CREATE INDEX idx_asset_playlist ON asset(playlist);
CREATE TABLE asset_sync (
id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+ asset_id BIGINT,
catalog_id VARCHAR(20) NOT NULL,
created TIMESTAMP NOT NULL,
sync_type VARCHAR(6) NOT NULL,
CREATE TABLE playlist_sync (
id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
- name VARCHAR(100) NOT NULL,
+ name VARCHAR(255) NOT NULL,
created TIMESTAMP NOT NULL,
sync_type VARCHAR(6) NOT NULL,
exported_success TIMESTAMP,
import lombok.*;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.Id;
+import javax.persistence.*;
import java.io.Serializable;
import java.sql.Date;
@AllArgsConstructor
public class Asset implements Serializable {
@Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
private String catalogId;
private Long seasonId;
@Column(nullable = false)
private String playlist;
+ @Column(length = 128)
private String hubInfo;
- @Column(nullable = false)
+ @Column(nullable = false, length = 250)
private String progLocalTitle;
+ @Column(length = 250)
+ private String progSynopsisShortHun;
+
+ @Column(length = 5000)
+ private String progSynopsisFullHun;
+
+ @Column(length = 128)
private String seasonLocalTitle;
+ @Column(length = 128)
private String seriesLocalTitle;
private Integer seasonNr;
private String contentSource;
private String ageRating;
+
+ @Column(length = 128)
+ private String director;
+
+ @Column(length = 128)
+ private String xCast;
+
+ @Column(name = "prog_genre1", length = 128)
+ private String progGenre1;
+
+ @Column(name = "prog_genre2", length = 128)
+ private String progGenre2;
+
+ @Column(name = "prog_genre3", length = 128)
+ private String progGenre3;
+
}
public class AssetSync implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
- Long id;
+ private Long id;
+
+ private Long assetId;
private String catalogId;
public class Associate implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
- Long id;
- String name;
- String login;
- String password;
- boolean active;
+ private Long id;
+ private String name;
+ private String login;
+ private String password;
+ private boolean active;
}
public class PlaylistSync implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
- Long id;
+ private Long id;
private String name;
private Date exportedError;
@Convert(converter = StringListConverter.class)
- private List<String> ids;
+ private List<Long> ids;
}
public class UploadFile implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
- Long id;
+ private Long id;
@CreationTimestamp
@Column(updatable = false)
- Date created;
+ private Date created;
- String name;
+ private String name;
- int size;
+ private int size;
- byte[] file;
+ private byte[] file;
}
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
@Converter
-public class StringListConverter implements AttributeConverter<List<String>, byte[]> {
+public class StringListConverter implements AttributeConverter<List<Long>, byte[]> {
private static final String SPLIT_CHAR = ",";
@Override
- public byte[] convertToDatabaseColumn(List<String> dataList) {
- return Objects.isNull(dataList) ? null : String.join(SPLIT_CHAR, dataList).getBytes();
+ public byte[] convertToDatabaseColumn(List<Long> ids) {
+ if (Objects.isNull(ids) || ids.isEmpty()) {
+ return null;
+ }
+ List<String> converted = ids.stream().map(String::valueOf).collect(Collectors.toList());
+ return String.join(SPLIT_CHAR, converted).getBytes();
}
@Override
- public List<String> convertToEntityAttribute(byte[] data) {
+ public List<Long> convertToEntityAttribute(byte[] data) {
+ if (data == null) {
+ return null;
+ }
String stringData = new String(data);
- return Arrays.asList(stringData.split(SPLIT_CHAR));
+ List<String> ids = Arrays.asList(stringData.split(SPLIT_CHAR));
+ return ids.stream().map(Long::parseLong).collect(Collectors.toList());
}
}
\ No newline at end of file
import java.sql.Date;
import java.util.List;
+import java.util.Optional;
-public interface AssetRepository extends JpaRepository<Asset, String> {
+public interface AssetRepository extends JpaRepository<Asset, Long> {
@Query("SELECT DISTINCT(a.playlist) FROM Asset a ORDER BY a.playlist")
List<String> queryDistinctPlaylists();
- @Query("SELECT a.catalogId FROM Asset a WHERE a.playlist = :playlist ORDER BY a.seasonNr, a.episodeNr")
- List<String> queryPlaylistsAssets(String playlist);
+ @Query("SELECT a.id FROM Asset a WHERE a.playlist = :playlist ORDER BY a.seasonNr, a.episodeNr")
+ List<Long> queryPlaylistsAssets(String playlist);
- @Query("SELECT a.catalogId FROM Asset a WHERE a.sunset <= :currentDate ORDER BY a.catalogId")
- List<String> queryExpired(Date currentDate);
+ Optional<Asset> findByCatalogIdAndPlaylist(String catalogId, String playlist);
+
+ @Query("SELECT a FROM Asset a WHERE a.sunset <= :currentDate ORDER BY a.catalogId")
+ List<Asset> queryExpired(Date currentDate);
@Modifying
@Query("DELETE FROM Asset a WHERE a.sunset <= :currentDate")
void deleteExpired(Date currentDate);
- @Query("SELECT a.referenceId FROM Asset a WHERE a.catalogId = :catalogId")
- String findReferenceIdByCatalogId(String catalogId);
+ @Query("SELECT a.referenceId FROM Asset a WHERE a.id = :id")
+ String findReferenceIdById(Long id);
+
+ @Query("SELECT a.catalogId FROM Asset a WHERE a.id = :id")
+ String findCatalogIdById(Long id);
- @Query("SELECT a.catalogId FROM Asset a WHERE a.referenceId = :referenceId")
- String findCatalogIdByReferenceId(String referenceId);
+ @Query("SELECT a.id FROM Asset a WHERE a.referenceId = :referenceId AND a.playlist = :playlist")
+ String findIdByReferenceId(String referenceId, String playlist);
}
long countBySyncType(SyncType syncType);
+ long countByExportedSuccessIsNull();
+
@Query("SELECT a.id FROM AssetSync a WHERE a.exportedSuccess IS NULL ORDER BY a.created")
List<Long> queryIdsForExport();
long countBySyncType(SyncType syncType);
+ long countByExportedSuccessIsNull();
+
List<PlaylistSync> findAllByName(String name);
@Query("SELECT p.id FROM PlaylistSync p WHERE p.exportedSuccess IS NULL ORDER BY p.created")
private boolean targetPlaylistInsertEnabled;
private Scheduler scheduler;
+ private Report report;
@Getter
@Setter
private String exportCron;
- private boolean sunriseCheckerEnabled;
+ private boolean sunsetCheckerEnabled;
+
+ private String sunsetCheckerCron;
+ }
+
+ @Getter
+ @Setter
+ public static final class Report {
+ private String sender;
+ private String recipient;
- private String sunriseCheckerCron;
}
}
package hu.user.mcvodsync.service.config;
+import hu.user.mcvodsync.db.repository.AssetSyncRepository;
+import hu.user.mcvodsync.db.repository.PlaylistSyncRepository;
import hu.user.mcvodsync.service.ServiceProperties;
+import hu.user.mcvodsync.service.data.CompositeSummary;
+import hu.user.mcvodsync.service.event.ExportCompletedEvent;
+import hu.user.mcvodsync.service.event.ExportStartedEvent;
import hu.user.mcvodsync.service.event.ImportCompletedEvent;
import hu.user.mcvodsync.service.event.ImportStartedEvent;
-import hu.user.mcvodsync.service.in.ImportSummary;
+import hu.user.mcvodsync.service.mail.EmailSendService;
import hu.user.mcvodsync.service.schedule.ScheduledExport;
import hu.user.mcvodsync.service.schedule.ScheduledImport;
-import hu.user.mcvodsync.service.schedule.ScheduledSunriseChacker;
+import hu.user.mcvodsync.service.schedule.ScheduledSunsetChacker;
+import hu.user.mcvodsync.service.time.TimeUtils;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
private ScheduledImport scheduledImport;
@Autowired
- private ScheduledSunriseChacker scheduledSunriseChacker;
+ private ScheduledSunsetChacker scheduledSunsetChacker;
+
+ @Autowired
+ private AssetSyncRepository assetSyncRepository;
+
+ @Autowired
+ private PlaylistSyncRepository playlistSyncRepository;
+
+ @Autowired
+ EmailSendService emailSendService;
@Getter
- private ImportSummary importSummary;
+ private CompositeSummary importSummary;
+
+ @Getter
+ private CompositeSummary exportSummary;
@Getter
private String status;
+ @Getter
+ private boolean outOfSync;
+
@PostConstruct
public void scheduleTasks() {
//new CronTrigger("0 0 4 1/1 * ? *")
if (scheduler.isImportEnabled()) {
taskScheduler.schedule(scheduledImport, new CronTrigger(scheduler.getExportCron()));
}
- if (scheduler.isSunriseCheckerEnabled()) {
- taskScheduler.schedule(scheduledSunriseChacker, new CronTrigger(scheduler.getSunriseCheckerCron()));
+ if (scheduler.isSunsetCheckerEnabled()) {
+ taskScheduler.schedule(scheduledSunsetChacker, new CronTrigger(scheduler.getSunsetCheckerCron()));
}
+
+ checkSyncStatus();
}
public void handleEvent(ImportStartedEvent evt) {
log.info("ImportStartedEvent handle");
importSummary = null;
- status = String.format("Import from %s started", evt.getFileName());
+ status = String.format("Import from %s is in progress, started as %s", evt.getFileName(), TimeUtils.toString(evt.getTimestamp()));
}
@Async
public void handleEvent(ImportCompletedEvent evt) {
log.info("ImportCompletedEvent handle");
importSummary = evt.getSummary();
- status = "Import completed";
+ status = String.format("Import completed at %s", TimeUtils.toString(evt.getTimestamp()));
+ checkSyncStatus();
+ emailSendService.sendSimpleMessage("BrightCove import report", importSummary);
+ }
+
+ @Async
+ @EventListener
+ public void handleEvent(ExportStartedEvent evt) {
+ log.info("ExportStartedEvent handle");
+ exportSummary = null;
+ status = String.format("Export is in progress, started at %s", TimeUtils.toString(evt.getTimestamp()));
+ }
+
+ @Async
+ @EventListener
+ public void handleEvent(ExportCompletedEvent evt) {
+ log.info("ExportCompletedEvent handle");
+ exportSummary = evt.getSummary();
+ status = String.format("Export completed at %s", TimeUtils.toString(evt.getTimestamp()));
+ checkSyncStatus();
+ emailSendService.sendSimpleMessage("BrightCove export report", importSummary);
+ }
+
+ public void reset() {
+ importSummary = null;
+ exportSummary = null;
+ checkSyncStatus();
+ }
+
+
+ public void checkSyncStatus() {
+ outOfSync = assetSyncRepository.countByExportedSuccessIsNull() > 0
+ || playlistSyncRepository.countByExportedSuccessIsNull() > 0;
}
}
--- /dev/null
+package hu.user.mcvodsync.service.data;
+
+import hu.user.mcvodsync.service.time.TimeUtils;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DurationFormatUtils;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Objects;
+
+@Getter
+@Setter
+@Builder
+public class CompositeSummary {
+ @Builder.Default
+ private Summary video = Summary.builder().build();
+
+ @Builder.Default
+ private Summary playlist = Summary.builder().build();
+
+ private Instant started;
+
+ private Instant finished;
+
+ public String toString() {
+ return String.format("Execution started at %s, finished at %s.", getStartedFormatted(), getFinishedFormatted()) +
+ System.lineSeparator() +
+ String.format("Process execution took %s, processed %d videos and %d playlists.", getDuration(), video.getAll(), playlist.getAll()) +
+ System.lineSeparator() +
+ String.format("Video success count: %d, error count: %d, skip count: %d", video.getSuccess(), video.getError(), video.getSkip()) +
+ System.lineSeparator() +
+ String.format("Video insert count: %d, update count: %d, delete count: %d", video.getInsert(), video.getUpdate(), video.getDelete()) +
+ System.lineSeparator() +
+ String.format("Playlist success count: %d, error count: %d, skip count: %d", playlist.getSuccess(), playlist.getError(), playlist.getSkip()) +
+ System.lineSeparator() +
+ String.format("Playlist insert count: %d, update count: %d, delete count: %d", playlist.getInsert(), playlist.getUpdate(), playlist.getDelete());
+ }
+
+ public String getDuration() {
+ Duration executionDuration = Duration.between(started, finished);
+ return DurationFormatUtils.formatDuration(executionDuration.toMillis(), "H:mm:ss", true);
+ }
+
+ public String getStartedFormatted() {
+ return Objects.isNull(started) ? StringUtils.EMPTY : TimeUtils.toString(started.toEpochMilli());
+ }
+
+ public String getFinishedFormatted() {
+ return Objects.isNull(finished) ? StringUtils.EMPTY : TimeUtils.toString(finished.toEpochMilli());
+ }
+
+
+}
--- /dev/null
+package hu.user.mcvodsync.service.data;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+public class Summary {
+ private long all;
+
+ private long success;
+
+ private long error;
+
+ private long skip;
+
+ private long insert;
+
+ private long update;
+
+ private long delete;
+
+ public void incAll() {
+ all++;
+ }
+
+ public void incSuccess() {
+ success++;
+ }
+
+ public void incError() {
+ error++;
+ }
+
+ public void incSkip() {
+ skip++;
+ }
+
+ public void incInsert() {
+ insert++;
+ }
+
+ public void incUpdate() {
+ update++;
+ }
+
+ public void incDelete() {
+ update++;
+ }
+
+}
package hu.user.mcvodsync.service.event;
-import hu.user.mcvodsync.service.in.ImportSummary;
+import hu.user.mcvodsync.service.data.CompositeSummary;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
@Setter
public class ExportCompletedEvent extends ApplicationEvent {
- private final ImportSummary summary;
+ private final CompositeSummary summary;
- public ExportCompletedEvent(ImportSummary summary, Object source) {
+ public ExportCompletedEvent(Object source, CompositeSummary summary) {
super(source);
this.summary = summary;
}
--- /dev/null
+package hu.user.mcvodsync.service.event;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.context.ApplicationEvent;
+
+@Getter
+@Setter
+public class ExportStartedEvent extends ApplicationEvent {
+
+ public ExportStartedEvent(Object source) {
+ super(source);
+ }
+
+}
package hu.user.mcvodsync.service.event;
-import hu.user.mcvodsync.service.in.ImportSummary;
+import hu.user.mcvodsync.service.data.CompositeSummary;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
@Setter
public class ImportCompletedEvent extends ApplicationEvent {
- private final ImportSummary summary;
+ private final CompositeSummary summary;
- public ImportCompletedEvent(Object source, ImportSummary summary) {
+ public ImportCompletedEvent(Object source, CompositeSummary summary) {
super(source);
this.summary = summary;
}
import hu.user.mcvodsync.db.repository.AssetRepository;
import hu.user.mcvodsync.db.repository.AssetSyncRepository;
import hu.user.mcvodsync.db.repository.PlaylistSyncRepository;
+import hu.user.mcvodsync.service.data.CompositeSummary;
import hu.user.mcvodsync.service.data.EntityDataService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections4.CollectionUtils;
@Autowired
private EntityDataService<Asset> entityDataService;
- private Map<String, List<String>> currentPlaylists;
+ private Map<String, List<Long>> currentPlaylists;
- private ImportSummary summary;
- public void prepare(ImportSummary summary) {
- this.summary = summary;
+ public void prepare() {
currentPlaylists = getPlayLists();
}
- public Map<String, List<String>> getPlayLists() {
- Map<String, List<String>> result = new HashMap<>();
+ public Map<String, List<Long>> getPlayLists() {
+ Map<String, List<Long>> result = new HashMap<>();
List<String> playlistNames = assetRepository.queryDistinctPlaylists();
playlistNames.forEach(playlist -> {
- List<String> playlistsAssets = assetRepository.queryPlaylistsAssets(playlist);
+ List<Long> playlistsAssets = assetRepository.queryPlaylistsAssets(playlist);
result.put(playlist, playlistsAssets);
});
return result;
}
- public void processAsset(Asset asset) {
- Optional<Asset> optionalEntity = assetRepository.findById(asset.getCatalogId());
+ public void processAsset(Asset asset, CompositeSummary summary) {
+ Optional<Asset> optionalEntity = assetRepository.findByCatalogIdAndPlaylist(asset.getCatalogId(), asset.getPlaylist());
AssetSync assetSync = AssetSync.builder()
.catalogId(asset.getCatalogId())
.build();
if (optionalEntity.isPresent()) {
+ asset.setId(optionalEntity.get().getId());
+ asset.setReferenceId(optionalEntity.get().getReferenceId());
boolean areDifferent = entityDataService.areDifferent(asset, optionalEntity.get());
if (areDifferent) {
assetSync.setSyncType(SyncType.UPDATE);
assetSync.setSyncType(SyncType.INSERT);
}
- if (Objects.nonNull(assetSync.getSyncType())) {
- assetRepository.saveAndFlush(asset);
- assetSyncRepository.saveAndFlush(assetSync);
- }
+ if (new Date().after(asset.getSunset())) {
+ summary.getVideo().incSkip();
+ if (assetSync.getSyncType() == SyncType.UPDATE) {
+ assetRepository.saveAndFlush(asset);
+ }
+ } else {
+ if (Objects.nonNull(assetSync.getSyncType())) {
+ assetRepository.saveAndFlush(asset);
+ assetSync.setAssetId(asset.getId());
+ assetSyncRepository.saveAndFlush(assetSync);
+ }
- if (assetSync.getSyncType() == SyncType.UPDATE) {
- summary.incUpdated();
- }
- if (assetSync.getSyncType() == SyncType.INSERT) {
- summary.incInserted();
+ if (assetSync.getSyncType() == SyncType.UPDATE) {
+ summary.getVideo().incUpdate();
+ }
+ if (assetSync.getSyncType() == SyncType.INSERT) {
+ summary.getVideo().incInsert();
+ }
}
}
- public void removeExpired(LocalDate currentDate) {
- List<String> ids = assetRepository.queryExpired(java.sql.Date.valueOf(currentDate));
+ public void removeExpired(LocalDate currentDate, CompositeSummary summary) {
+ List<Asset> expiredAssets = assetRepository.queryExpired(java.sql.Date.valueOf(currentDate));
assetRepository.deleteExpired(java.sql.Date.valueOf(currentDate));
- List<AssetSync> syncs = ids.stream().map(id -> AssetSync.builder()
- .catalogId(id)
+ List<AssetSync> syncs = expiredAssets.stream().map(asset -> AssetSync.builder()
+ .assetId(asset.getId())
+ .catalogId(asset.getCatalogId())
.syncType(SyncType.DELETE)
.build()).collect(Collectors.toList());
assetSyncRepository.saveAllAndFlush(syncs);
- summary.setDeleted(summary.getDeleted() + ids.size());
+ summary.getVideo().setDelete(summary.getVideo().getDelete() + expiredAssets.size());
}
- public void finish() {
- removeExpired(LocalDate.now());
- processPlaylists();
+ public void finish(CompositeSummary summary) {
+ removeExpired(LocalDate.now(), summary);
+ processPlaylists(summary);
}
- public void processPlaylists() {
- Map<String, List<String>> newPlaylists = getPlayLists();
+ public void processPlaylists(CompositeSummary summary) {
+ Map<String, List<Long>> newPlaylists = getPlayLists();
Set<String> currentPlaylistNames = currentPlaylists.keySet();
Set<String> newPlaylistNames = newPlaylists.keySet();
.created(Date.from(Instant.now()))
.build();
playlistSyncRepository.save(sync);
- summary.incDeletedPlaylist();
+ summary.getPlaylist().incDelete();
});
}
.ids(newPlaylists.get(name))
.build();
playlistSyncRepository.save(sync);
- summary.incInsertedPlaylist();
+ summary.getPlaylist().incInsert();
});
}
currentPlaylistNames.forEach(name -> {
- List<String> currentContent = currentPlaylists.get(name);
- List<String> newContent = newPlaylists.get(name);
+ List<Long> currentContent = currentPlaylists.get(name);
+ List<Long> newContent = newPlaylists.get(name);
if (isContentChanged(currentContent, newContent)) {
PlaylistSync sync = PlaylistSync.builder()
.name(name)
.created(Date.from(Instant.now()))
.build();
playlistSyncRepository.save(sync);
- summary.incUpdatedPlaylist();
+ summary.getPlaylist().incUpdate();
}
});
}
- private boolean isContentChanged(List<String> currentContent, List<String> newContent) {
+ private boolean isContentChanged(List<Long> currentContent, List<Long> newContent) {
if (currentContent.size() != newContent.size()) {
return true;
}
@Mapping(target = "playlist", source = "PLAYLIST_NAME")
@Mapping(target = "hubInfo", source = "HUB_INFO")
@Mapping(target = "progLocalTitle", source = "PROG_LOCAL_TITLE")
+ @Mapping(target = "progSynopsisShortHun", source = "PROG_SYNOPSIS_SHORT_HUN")
+ @Mapping(target = "progSynopsisFullHun", source = "PROG_SYNOPSIS_FULL_HUN")
@Mapping(target = "seasonLocalTitle", source = "SEASON_LOCAL_TITLE")
@Mapping(target = "seriesLocalTitle", source = "SERIES_GROUP_LOCAL_TITLE")
@Mapping(target = "seasonNr", source = "SEASON_NUMBER", qualifiedByName = "mapNullableNumber")
@Mapping(target = "contentType", source = "CONTENT_TYPE")
@Mapping(target = "contentSource", source = "CONTENT_SOURCE")
@Mapping(target = "ageRating", source = "AGE_RATING")
+ @Mapping(target = "director", source = "DIRECTOR")
+ @Mapping(target = "xCast", source = "X_CAST")
+ @Mapping(target = "progGenre1", source = "PROG_GENRE_1")
+ @Mapping(target = "progGenre2", source = "PROG_GENRE_2")
+ @Mapping(target = "progGenre3", source = "PROG_GENRE_3")
Asset toEntity(Map<String, String> xlsRowData);
@Named("mapNullableNumber")
+++ /dev/null
-package hu.user.mcvodsync.service.in;
-
-import lombok.Builder;
-import lombok.Getter;
-import lombok.Setter;
-import org.apache.commons.lang3.time.DurationFormatUtils;
-
-import java.time.Duration;
-import java.time.Instant;
-
-@Getter
-@Setter
-@Builder
-public class ImportSummary {
- private Instant started;
-
- private Instant finished;
-
- private long all;
-
- private long success;
-
- private long error;
-
- private long inserted;
-
- private long updated;
-
- private long deleted;
-
- private long insertedPlaylist;
-
- private long updatedPlaylist;
-
- private long deletedPlaylist;
-
- public String toString() {
- Duration executionDuration = Duration.between(started, finished);
- String duration = DurationFormatUtils.formatDuration(executionDuration.toMillis(), "H:mm:ss", true);
- return String.format("Execution started at %s, finished at %s.", started, finished) +
- System.lineSeparator() +
- String.format("Process execution took %s on %d rows.", duration, all) +
- System.lineSeparator() +
- String.format("Success count: %d, error count: %d", success, error) +
- System.lineSeparator() +
- String.format("Insert count: %d, update count: %d, delete count: %d", inserted, updated, deleted);
- }
-
- public void incAll() {
- all++;
- }
-
- public void incSuccess() {
- success++;
- }
-
- public void incError() {
- error++;
- }
-
- public void incInserted() {
- inserted++;
- }
-
- public void incUpdated() {
- updated++;
- }
-
- public void incInsertedPlaylist() {
- insertedPlaylist++;
- }
-
- public void incUpdatedPlaylist() {
- updatedPlaylist++;
- }
-
- public void incDeletedPlaylist() {
- deletedPlaylist++;
- }
-
-}
package hu.user.mcvodsync.service.in;
import hu.user.mcvodsync.db.Asset;
+import hu.user.mcvodsync.service.data.CompositeSummary;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.dhatim.fastexcel.reader.Cell;
import org.dhatim.fastexcel.reader.Row;
import org.dhatim.fastexcel.reader.Sheet;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
+import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayInputStream;
import java.util.stream.Stream;
@Log4j2
-@Service
+@Component
public class VodXlsProcessor {
private List<Cell> headers;
@Autowired
private AssetImportService assetImportService;
+
@Transactional
- public ImportSummary process(String fileName, byte[] xlsData) {
- ImportSummary summary = ImportSummary.builder().started(Instant.now()).build();
+ public CompositeSummary process(String fileName, byte[] xlsData) {
+ CompositeSummary summary = CompositeSummary.builder().started(Instant.now()).build();
try (InputStream is = new ByteArrayInputStream(xlsData);
ReadableWorkbook wb = new ReadableWorkbook(is)) {
Sheet sheet = wb.getFirstSheet();
try (Stream<Row> rows = sheet.openStream()) {
rows.forEach(row -> {
if (row.getRowNum() == 1) {
- assetImportService.prepare(summary);
+ assetImportService.prepare();
headers = getHeaders(row);
} else {
- summary.incAll();
+ log.info("Processing {}", row.getRowNum());
+ summary.getVideo().incAll();
processRow(row, summary);
}
});
}
- assetImportService.finish();
+ assetImportService.finish(summary);
} catch (IOException e) {
log.error("Excel file reading error from {}. System message: {}", fileName, e.getMessage());
return row.stream().collect(Collectors.toList());
}
- private void processRow(Row r, ImportSummary summary) {
+ private void processRow(Row r, CompositeSummary summary) {
Map<String, String> rowData = new HashMap<>();
r.stream().filter(Objects::nonNull).forEach(c -> {
try {
try {
asset = assetMapper.toEntity(rowData);
if (isAssetValid(asset)) {
- assetImportService.processAsset(asset);
+ assetImportService.processAsset(asset, summary);
+ } else {
+ summary.getVideo().incSkip();
}
- summary.incSuccess();
+ summary.getVideo().incSuccess();
} catch (Exception e) {
- summary.incError();
+ summary.getVideo().incError();
log.error("Processing error for {}", asset, e);
}
}
package hu.user.mcvodsync.service.mail;
+import hu.user.mcvodsync.service.ServiceProperties;
+import hu.user.mcvodsync.service.data.CompositeSummary;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
+import java.util.Objects;
+
+@Log4j2
@Service
public class EmailSendService {
// https://www.baeldung.com/spring-email
- @Autowired
+ @Autowired(required = false)
private JavaMailSender emailSender;
- public void sendSimpleMessage() {
+ @Autowired
+ private ServiceProperties serviceProperties;
+
+ public void sendSimpleMessage(String subject, CompositeSummary summary) {
+ if (Objects.isNull(emailSender)) {
+ log.warn("Missing email settings, can not initiate JavaMailSender");
+ return;
+ }
+ if (StringUtils.isBlank(serviceProperties.getReport().getRecipient())) {
+ log.info("No recipient specified, report not sent.");
+ return;
+ }
SimpleMailMessage message = new SimpleMailMessage();
- message.setFrom("noreply@useribm.hu");
- message.setTo("vasary@elgekko.net");
- message.setSubject("Teszt");
- message.setText("Teszt1");
+ message.setFrom(serviceProperties.getReport().getSender());
+ message.setTo(serviceProperties.getReport().getRecipient());
+ message.setSubject(subject);
+ message.setText(summary.toString());
emailSender.send(message);
+ log.info("'{}' report sent to {}", subject, serviceProperties.getReport().getRecipient());
}
}
import com.brightcove.cms.client.model.Playlist;
import com.brightcove.cms.client.model.Video;
-import com.google.common.collect.Lists;
import hu.user.mcvodsync.brightcove.BrightCoveClient;
import hu.user.mcvodsync.db.Asset;
import hu.user.mcvodsync.db.AssetSync;
import hu.user.mcvodsync.db.repository.AssetRepository;
import hu.user.mcvodsync.db.repository.AssetSyncRepository;
import hu.user.mcvodsync.db.repository.PlaylistSyncRepository;
-import hu.user.mcvodsync.service.ServiceProperties;
+import hu.user.mcvodsync.service.data.CompositeSummary;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.EntityNotFoundException;
import java.time.Instant;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@Log4j2
@Service
-public class AssetExportService {
+public class SyncExportService {
@Autowired
private BrightCoveClient bcClient;
@Autowired
private VideoMapper videoMapper;
- @Autowired
- private ServiceProperties serviceProperties;
-
- private static final int ASSET_PROCESSING_LIMIT = 100;
- private static final int PLAYLIST_PROCESSING_LIMIT = 10;
-
- public void export() {
- exportAssets();
- exportPlaylists();
- }
-
- private void exportAssets() {
- List<Long> ids = assetSyncRepository.queryIdsForExport();
- if (ids.isEmpty()) {
- log.info("There are no video changes to export");
- return;
- }
- List<List<Long>> partitions = Lists.partition(ids, ASSET_PROCESSING_LIMIT);
- partitions.forEach(currentIds -> {
- List<AssetSync> result = assetSyncRepository.findAllByIdInOrderByCreated(currentIds);
- if (serviceProperties.isTargetVideoInsertEnabled()) {
- result.forEach(this::processAssetSync);
- } else {
- result.forEach(this::processAssetSyncWithoutInsert);
- }
- });
- }
+ private static final String MC_VOD_SYNC = "mc-vod-sync";
@Transactional
- public void processAssetSyncWithoutInsert(AssetSync assetSync) {
+ public void processAssetSyncWithoutInsert(AssetSync assetSync, CompositeSummary summary) {
try {
if (SyncType.DELETE.equals(assetSync.getSyncType())) {
//delete not necessary, remove only from all playlists
assetSync.setExportedSuccess(Date.from(Instant.now()));
+ summary.getVideo().incDelete();
} else {
- Asset asset = assetRepository.findById(assetSync.getCatalogId()).orElseThrow(EntityNotFoundException::new);
+ Asset asset = assetRepository.findById(assetSync.getAssetId()).orElseThrow(EntityNotFoundException::new);
Video assetVideo = videoMapper.toVideo(asset);
String videoId = asset.getReferenceId();
if (StringUtils.isBlank(videoId)) {
Video video = bcClient.getVideoByReferenceId(asset.getCatalogId());
- videoId = video.getId();
- asset.setReferenceId(videoId);
- assetRepository.save(asset);
+ if (Objects.isNull(video)) {
+ summary.getVideo().incSkip();
+ } else {
+ videoId = video.getId();
+ asset.setReferenceId(videoId);
+ assetRepository.save(asset);
+ }
+ }
+ if (StringUtils.isNotBlank(videoId)) {
+ assetVideo.addTagsItem(MC_VOD_SYNC);
+ bcClient.updateVideo(videoId, assetVideo);
+ summary.getVideo().incUpdate();
}
- bcClient.updateVideo(videoId, assetVideo);
assetSync.setExportedSuccess(Date.from(Instant.now()));
}
+ summary.getVideo().incSuccess();
} catch (Exception e) {
log.error(e);
assetSync.setExportedError(Date.from(Instant.now()));
+ summary.getVideo().incError();
} finally {
assetSyncRepository.save(assetSync);
}
}
@Transactional
- public void processAssetSync(AssetSync assetSync) {
+ public void processAssetSync(AssetSync assetSync, CompositeSummary summary) {
try {
if (SyncType.DELETE.equals(assetSync.getSyncType())) {
//delete not necessary, remove only from all playlists
assetSync.setExportedSuccess(Date.from(Instant.now()));
+ summary.getVideo().incDelete();
} else {
- Asset asset = assetRepository.findById(assetSync.getCatalogId()).orElseThrow(EntityNotFoundException::new);
+ Asset asset = assetRepository.findById(assetSync.getAssetId()).orElseThrow(EntityNotFoundException::new);
+ //TODO handle deleted
+
Video assetVideo = videoMapper.toVideo(asset);
- assetVideo.setTags(Collections.singletonList("mc-vod-sync"));
+ assetVideo.addTagsItem(MC_VOD_SYNC);
+
if (SyncType.INSERT.equals(assetSync.getSyncType())) {
- Video createdVideo = bcClient.createVideo(assetVideo);
- asset.setReferenceId(createdVideo.getId());
+ Video existingAssetVideo = null;
+ if (StringUtils.isBlank(asset.getReferenceId())) {
+ existingAssetVideo = bcClient.getVideoByReferenceId(asset.getCatalogId());
+ }
+
+ if (Objects.isNull(existingAssetVideo)) {
+ Video createdVideo = bcClient.createVideo(assetVideo);
+ asset.setReferenceId(createdVideo.getId());
+ summary.getVideo().incInsert();
+ } else {
+ summary.getVideo().incUpdate();
+ asset.setReferenceId(existingAssetVideo.getId());
+ }
assetRepository.save(asset);
}
if (SyncType.UPDATE.equals(assetSync.getSyncType())) {
if (StringUtils.isBlank(asset.getReferenceId())) {
throw new IllegalArgumentException("Can not update Video when Asset entity reference ID is empty");
}
- bcClient.updateVideo(asset.getReferenceId(), assetVideo);
+
+ Video origVideo = bcClient.getVideoById(asset.getReferenceId());
+ origVideo.setName(assetVideo.getName());
+ bcClient.updateVideo(asset.getReferenceId(), origVideo);
+ summary.getVideo().incUpdate();
}
assetSync.setExportedSuccess(Date.from(Instant.now()));
+ summary.getVideo().incSuccess();
}
} catch (Exception e) {
log.error(e);
assetSync.setExportedError(Date.from(Instant.now()));
+ summary.getVideo().incError();
} finally {
assetSyncRepository.save(assetSync);
}
}
- private void exportPlaylists() {
- List<Long> ids = playlistSyncRepository.queryIdsForExport();
- if (ids.isEmpty()) {
- log.info("There are no playlist changes to export");
- return;
- }
- List<List<Long>> partitions = Lists.partition(ids, PLAYLIST_PROCESSING_LIMIT);
- partitions.forEach(currentIds -> {
- List<PlaylistSync> result = playlistSyncRepository.findAllByIdInOrderByCreated(currentIds);
- if (serviceProperties.isTargetPlaylistInsertEnabled()) {
- result.forEach(this::processPlaylistSync);
- } else {
- result.forEach(this::processPlaylistSyncWithoutInsert);
- }
- });
- }
-
-
@Transactional
- public void processPlaylistSync(PlaylistSync playlistSync) {
+ public void processPlaylistSync(PlaylistSync playlistSync, CompositeSummary summary) {
try {
if (SyncType.DELETE.equals(playlistSync.getSyncType())) {
playlistSync.setExportedSuccess(Date.from(Instant.now()));
+ summary.getPlaylist().incDelete();
} else {
- Playlist playlist = bcClient.getPlaylistsByName(playlistSync.getName());
+ Playlist playlist = bcClient.getPlaylistsById(playlistSync.getName());
//TODO check if all Asset has rferenceId
if (Objects.nonNull(playlist)) {
playlist.setVideoIds(toApiIds(playlistSync.getIds()));
bcClient.updatePlaylist(playlist);
+ summary.getPlaylist().incUpdate();
} else {
playlist = new Playlist();
playlist.setName(playlistSync.getName());
- playlist.setDescription("mc-vod-sync");
+ playlist.setDescription(MC_VOD_SYNC);
playlist.setVideoIds(toApiIds(playlistSync.getIds()));
bcClient.createPlaylist(playlist);
+ summary.getPlaylist().incInsert();
}
playlistSync.setExportedSuccess(Date.from(Instant.now()));
}
+ summary.getPlaylist().incSuccess();
} catch (Exception e) {
log.error(e);
playlistSync.setExportedError(Date.from(Instant.now()));
+ summary.getPlaylist().incError();
} finally {
playlistSyncRepository.save(playlistSync);
}
}
- private List<String> toApiIds(List<String> ids) {
- return ids.stream().map(assetRepository::findReferenceIdByCatalogId).collect(Collectors.toList());
- }
-
@Transactional
- public void processPlaylistSyncWithoutInsert(PlaylistSync playlistSync) {
+ public void processPlaylistSyncWithoutInsert(PlaylistSync playlistSync, CompositeSummary summary) {
try {
if (SyncType.DELETE.equals(playlistSync.getSyncType())) {
playlistSync.setExportedSuccess(Date.from(Instant.now()));
+ summary.getPlaylist().incDelete();
} else {
- Playlist playlist = bcClient.getPlaylistsByName(playlistSync.getName());
+ Playlist playlist = bcClient.getPlaylistsById(playlistSync.getName());
if (Objects.nonNull(playlist)) {
playlist.setVideoIds(toApiIds(playlistSync.getIds()));
bcClient.updatePlaylist(playlist);
+ summary.getPlaylist().incUpdate();
} else {
log.warn("Playlist not found: {}", playlistSync.getName());
+ summary.getPlaylist().incSkip();
}
playlistSync.setExportedSuccess(Date.from(Instant.now()));
}
+ summary.getPlaylist().incSuccess();
} catch (Exception e) {
log.error(e);
playlistSync.setExportedError(Date.from(Instant.now()));
+ summary.getPlaylist().incError();
+ } finally {
+ playlistSyncRepository.save(playlistSync);
}
}
+
+ private List<String> toApiIds(List<Long> ids) {
+ return ids.stream().map(assetRepository::findReferenceIdById).collect(Collectors.toList());
+ }
}
--- /dev/null
+package hu.user.mcvodsync.service.out;
+
+
+import com.google.common.collect.Lists;
+import hu.user.mcvodsync.db.AssetSync;
+import hu.user.mcvodsync.db.PlaylistSync;
+import hu.user.mcvodsync.db.repository.AssetSyncRepository;
+import hu.user.mcvodsync.db.repository.PlaylistSyncRepository;
+import hu.user.mcvodsync.service.ServiceProperties;
+import hu.user.mcvodsync.service.data.CompositeSummary;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.time.Instant;
+import java.util.List;
+
+@Log4j2
+@Component
+public class SyncProcessor {
+
+ @Autowired
+ private AssetSyncRepository assetSyncRepository;
+
+ @Autowired
+ private PlaylistSyncRepository playlistSyncRepository;
+
+ @Autowired
+ private ServiceProperties serviceProperties;
+
+ @Autowired
+ private SyncExportService syncExportService;
+
+ private static final int ASSET_PROCESSING_LIMIT = 100;
+ private static final int PLAYLIST_PROCESSING_LIMIT = 10;
+
+
+ public CompositeSummary export() {
+ CompositeSummary summary = CompositeSummary.builder().started(Instant.now()).build();
+ exportAssets(summary);
+ exportPlaylists(summary);
+ summary.setFinished(Instant.now());
+ log.info(summary);
+ return summary;
+ }
+
+ private void exportAssets(CompositeSummary summary) {
+ List<Long> ids = assetSyncRepository.queryIdsForExport();
+ if (ids.isEmpty()) {
+ log.info("There are no video changes to export");
+ return;
+ }
+ List<List<Long>> partitions = Lists.partition(ids, ASSET_PROCESSING_LIMIT);
+ partitions.forEach(currentIds -> {
+ summary.getVideo().incAll();
+ List<AssetSync> result = assetSyncRepository.findAllByIdInOrderByCreated(currentIds);
+ if (serviceProperties.isTargetVideoInsertEnabled()) {
+ result.forEach(sync -> syncExportService.processAssetSync(sync, summary));
+ } else {
+ result.forEach(sync -> syncExportService.processAssetSyncWithoutInsert(sync, summary));
+ }
+ });
+ }
+
+ private void exportPlaylists(CompositeSummary summary) {
+ List<Long> ids = playlistSyncRepository.queryIdsForExport();
+ if (ids.isEmpty()) {
+ log.info("There are no playlist changes to export");
+ return;
+ }
+ List<List<Long>> partitions = Lists.partition(ids, PLAYLIST_PROCESSING_LIMIT);
+ partitions.forEach(currentIds -> {
+ summary.getPlaylist().incAll();
+ List<PlaylistSync> result = playlistSyncRepository.findAllByIdInOrderByCreated(currentIds);
+ if (serviceProperties.isTargetPlaylistInsertEnabled()) {
+ result.forEach(sync -> syncExportService.processPlaylistSync(sync, summary));
+ } else {
+ result.forEach(sync -> syncExportService.processPlaylistSyncWithoutInsert(sync, summary));
+ }
+ });
+ }
+
+}
import com.brightcove.cms.client.model.Video;
import hu.user.mcvodsync.db.Asset;
+import org.apache.commons.lang3.StringUtils;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface VideoMapper {
-
- String NOTES = "notes";
- String SHOW = "show";
- String SEASON = "season";
- String EPISODE = "episode";
- String RATING_TV = "rating_tv";
- String PRODUCTION_YEAR = "production_year";
+ String CUSTOM_NOTES = "notes";
+ String CUSTOM_SHOW = "show";
+ String CUSTOM_SEASON = "season";
+ String CUSTOM_EPISODE = "episode";
+ String CUSTOM_RATING_TV = "rating_tv";
+ String CUSTOM_PRODUCTION_YEAR = "production_year";
String ACTORS_TALENT = "actors_talent";
String DIRECTOR = "director";
String GENRE = "genre";
Video result = new Video();
result.setReferenceId(asset.getCatalogId());
result.setName(asset.getProgLocalTitle());
- result.putCustomFieldsItem(NOTES, asset.getHubInfo());
- result.putCustomFieldsItem(SHOW, asset.getSeasonLocalTitle());
+ result.setLongDescription(asset.getProgSynopsisFullHun());
+ result.setDescription(asset.getProgSynopsisShortHun());
+ result.putCustomFieldsItem(CUSTOM_NOTES, asset.getHubInfo());
+ result.putCustomFieldsItem(CUSTOM_SHOW, asset.getSeasonLocalTitle());
if (Objects.nonNull(asset.getSeasonNr())) {
- result.putCustomFieldsItem(SEASON, asset.getSeasonNr().toString());
+ result.putCustomFieldsItem(CUSTOM_SEASON, asset.getSeasonNr().toString());
}
if (Objects.nonNull(asset.getEpisodeNr())) {
- result.putCustomFieldsItem(EPISODE, asset.getEpisodeNr().toString());
+ result.putCustomFieldsItem(CUSTOM_EPISODE, asset.getEpisodeNr().toString());
}
- if (Objects.nonNull(asset.getAgeRating())
+ if (StringUtils.isNotBlank(asset.getAgeRating())
&& valid_AgeRating.contains(asset.getAgeRating())) {
- result.putCustomFieldsItem(RATING_TV, asset.getAgeRating());
+ result.putCustomFieldsItem(CUSTOM_RATING_TV, asset.getAgeRating());
}
if (Objects.nonNull(asset.getProductionYear())) {
- result.putCustomFieldsItem(PRODUCTION_YEAR, asset.getProductionYear().toString());
+ result.putCustomFieldsItem(CUSTOM_PRODUCTION_YEAR, asset.getProductionYear().toString());
+ }
+ if (StringUtils.isNotBlank(asset.getDirector())) {
+ result.putCustomFieldsItem(DIRECTOR, asset.getDirector());
+ }
+ if (StringUtils.isNotBlank(asset.getDirector())) {
+ result.putCustomFieldsItem(DIRECTOR, asset.getDirector());
+ }
+ if (StringUtils.isNotBlank(asset.getXCast())) {
+ result.putCustomFieldsItem(ACTORS_TALENT, asset.getXCast());
+ }
+ if (StringUtils.isNotBlank(asset.getProgGenre1())) {
+ result.addTagsItem(asset.getProgGenre1());
+ }
+ if (StringUtils.isNotBlank(asset.getProgGenre2())) {
+ result.addTagsItem(asset.getProgGenre2());
+ }
+ if (StringUtils.isNotBlank(asset.getProgGenre3())) {
+ result.addTagsItem(asset.getProgGenre3());
}
return result;
}
.referenceId(video.getId())
.catalogId(video.getReferenceId())
.progLocalTitle(video.getName())
+ .progSynopsisFullHun(video.getLongDescription())
+ .progSynopsisShortHun(video.getDescription())
.build();
Optional<Map<String, String>> opCustomFields = Optional.ofNullable(video.getCustomFields());
- opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(NOTES))).ifPresent(result::setHubInfo);
- opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(SHOW))).ifPresent(result::setSeasonLocalTitle);
- opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(SEASON))).ifPresent(val -> result.setSeasonNr(Integer.parseInt(val)));
- opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(EPISODE))).ifPresent(val -> result.setEpisodeNr(Integer.parseInt(val)));
- opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(RATING_TV))).ifPresent(result::setAgeRating);
- opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(PRODUCTION_YEAR))).ifPresent(val -> result.setProductionYear(Integer.parseInt(val)));
+ opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(CUSTOM_NOTES))).ifPresent(result::setHubInfo);
+ opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(CUSTOM_SHOW))).ifPresent(result::setSeasonLocalTitle);
+ opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(CUSTOM_SEASON))).ifPresent(val -> result.setSeasonNr(Integer.parseInt(val)));
+ opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(CUSTOM_EPISODE))).ifPresent(val -> result.setEpisodeNr(Integer.parseInt(val)));
+ opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(CUSTOM_RATING_TV))).ifPresent(result::setAgeRating);
+ opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(CUSTOM_PRODUCTION_YEAR))).ifPresent(val -> result.setProductionYear(Integer.parseInt(val)));
+ opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(DIRECTOR))).ifPresent(result::setDirector);
+ opCustomFields.flatMap(fields -> Optional.ofNullable(fields.get(ACTORS_TALENT))).ifPresent(result::setXCast);
return result;
}
+
}
package hu.user.mcvodsync.service.schedule;
+import hu.user.mcvodsync.service.data.CompositeSummary;
import hu.user.mcvodsync.service.event.ExportCompletedEvent;
-import hu.user.mcvodsync.service.out.AssetExportService;
+import hu.user.mcvodsync.service.event.ExportStartedEvent;
+import hu.user.mcvodsync.service.out.SyncProcessor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
public class ScheduledExport implements Runnable {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
-
+
@Autowired
- private AssetExportService assetExportService;
+ private SyncProcessor syncProcessor;
@SneakyThrows
@Override
public void run() {
+ CompositeSummary summary = null;
try {
+ applicationEventPublisher.publishEvent(new ExportStartedEvent(this));
log.info("ScheduledExport started");
- assetExportService.export();
+ summary = syncProcessor.export();
} catch (Exception e) {
log.error("ScheduledExport error!", e);
} finally {
- applicationEventPublisher.publishEvent(new ExportCompletedEvent(null, this));
- log.info("ScheduledImport finished");
+ applicationEventPublisher.publishEvent(new ExportCompletedEvent(this, summary));
+ log.info("ScheduledExport finished");
}
}
import hu.user.mcvodsync.db.UploadFile;
import hu.user.mcvodsync.db.repository.UploadFileRepository;
+import hu.user.mcvodsync.service.data.CompositeSummary;
import hu.user.mcvodsync.service.event.ImportCompletedEvent;
import hu.user.mcvodsync.service.event.ImportStartedEvent;
-import hu.user.mcvodsync.service.in.ImportSummary;
import hu.user.mcvodsync.service.in.VodXlsProcessor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
@Override
public void run() {
- ImportSummary summary = null;
+ CompositeSummary summary = null;
try {
Optional<UploadFile> opUploadFile = uploadFileRepository.findFirstByOrderByCreatedDesc();
if (opUploadFile.isPresent()) {
- log.info("ScheduledImport started from {}", opUploadFile.get().getName());
applicationEventPublisher.publishEvent(new ImportStartedEvent(this, opUploadFile.get().getName()));
+ log.info("ScheduledImport started from {}", opUploadFile.get().getName());
summary = vodXlsProcessor.process(opUploadFile.get().getName(), opUploadFile.get().getFile());
}
} catch (Exception e) {
@Log4j2
@Component
//@ConditionalOnExpression("'${mc-vod-sync.scheduler.sunrise-checker}'=='true'")
-public class ScheduledSunriseChacker implements Runnable {
+public class ScheduledSunsetChacker implements Runnable {
@SneakyThrows
@Override
--- /dev/null
+package hu.user.mcvodsync.service.time;
+
+import java.text.Format;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class TimeUtils {
+ private static final Format format = new SimpleDateFormat("yyyy.MM.dd. HH:mm:ss");
+
+ public static String toString(long timestamp) {
+ Date date = new Date(timestamp);
+ return format.format(date);
+ }
+}
public class ApplicationProperties {
String userName;
String password;
+ boolean admin;
}
public class Constants {
public static final String APPLICATION_NAME = "MC-VOD-SYNC";
- public static final String NAV_IMPORT_FILE = "/import-file";
+ public static final String NAV_MAIN = "/main";
public static final String NAV_BROWSE_REMOTE = "/browse-remote";
- public static final String NAV_EXPORT_DATA = "/export-data";
public static final String NAV_ASSOCIATES = "/associates";
public static final String NAV_LOGIN = "/login";
public static final String NAV_LOGOUT = "/logout";
return "login";
}
- @GetMapping({Constants.NAV_IMPORT_FILE, Constants.NAV_BROWSE_REMOTE, Constants.NAV_EXPORT_DATA, Constants.NAV_ASSOCIATES})
+ @GetMapping({Constants.NAV_MAIN, Constants.NAV_BROWSE_REMOTE, Constants.NAV_ASSOCIATES})
public String index() {
return "index";
}
.anyRequest().authenticated()
.and()
.formLogin()
- .loginPage(Constants.NAV_LOGIN).defaultSuccessUrl(Constants.NAV_IMPORT_FILE)
+ .loginPage(Constants.NAV_LOGIN).defaultSuccessUrl(Constants.NAV_MAIN)
.and()
.logout().logoutUrl(Constants.NAV_LOGOUT).logoutSuccessUrl(Constants.NAV_LOGIN)
.and()
@Log4j2
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class BrightCovePlaylistDataModel extends CachedSpringDataModel<Playlist> {
+
@Autowired
private BrightCoveClient brightCoveClient;
@Override
public List<Playlist> getResultSet(int page, int pageSize, FieldComparator sortComparator) {
- return brightCoveClient.getPlaylists(page * pageSize, pageSize, "description:mc-vod-sync");
+ //"description:mc-vod-sync"
+ return brightCoveClient.getPlaylists(page * pageSize, pageSize, null);
}
@Override
public int getResultSetCount() {
- return brightCoveClient.getPlaylistCount("description:mc-vod-sync");
+ return brightCoveClient.getPlaylistCount(null);
}
public void refresh() {
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.zkoss.zul.FieldComparator;
+import org.zkoss.zul.Messagebox;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
@Component
@Log4j2
return null;
}
List<List<String>> partitions = Lists.partition(playlist.getVideoIds(), pageSize);
- String query = StringUtils.join(partitions.get(page), ",");
- return brightCoveClient.getVideos(pageSize * page, pageSize, query);
+ List<String> currentIds = partitions.get(page);
+ String query = StringUtils.join(currentIds, ",");
+ List<Video> unsortedList = null;
+ try {
+ unsortedList = brightCoveClient.getVideos(pageSize * page, pageSize, query);
+ } catch (Exception e) {
+ Messagebox.show(e.getMessage());
+ ArrayList<Video> videos = new ArrayList<>();
+ for (int i = 0; i < playlist.getVideoIds().size(); i++) {
+ Video video = new Video();
+ video.setName("Error!");
+ videos.add(video);
+ }
+ return videos;
+ }
+ return sortVideos(page, unsortedList, currentIds);
+ }
+
+ private static List<Video> sortVideos(int page, List<Video> unsortedList, List<String> currentIds) {
+ List<Video> result = new ArrayList<>(unsortedList.size());
+ Map<String, Video> mappedVideos;
+ try {
+ mappedVideos = unsortedList.stream().collect(Collectors.toMap(Video::getId, Function.identity()));
+ currentIds.forEach(id -> result.add(mappedVideos.get(id)));
+ } catch (Exception e) {
+ log.error(e);
+ }
+ return result;
}
@Override
public int getResultSetCount() {
- if (Objects.isNull(playlist) || Objects.isNull(playlist.getVideoIds())) {
+ if (Objects.isNull(playlist)) {
return 0;
}
- return playlist.getVideoIds().size();
+
+ List<String> videoIds = null;
+ try {
+ videoIds = playlist.getVideoIds();
+ } catch (Exception e) {
+ log.error(e);
+ }
+
+ return Objects.isNull(videoIds) ? 0 : videoIds.size();
}
public void refresh(Playlist playlist) {
+++ /dev/null
-package hu.user.mcvodsync.ui.view;
-
-import hu.user.mcvodsync.service.config.ScheduledTasks;
-import hu.user.mcvodsync.service.event.ExportCompletedEvent;
-import hu.user.mcvodsync.ui.Constants;
-import lombok.Getter;
-import lombok.extern.log4j.Log4j2;
-import org.springframework.context.ApplicationListener;
-import org.zkoss.bind.annotation.Command;
-import org.zkoss.bind.annotation.Init;
-import org.zkoss.zk.ui.select.annotation.VariableResolver;
-import org.zkoss.zk.ui.select.annotation.WireVariable;
-import org.zkoss.zk.ui.util.Clients;
-import org.zkoss.zkplus.spring.DelegatingVariableResolver;
-
-@Getter
-@Log4j2
-@VariableResolver(DelegatingVariableResolver.class)
-public class ExportDataViewModel implements ApplicationListener<ExportCompletedEvent> {
-
- @WireVariable
- private ScheduledTasks scheduledTasks;
-
- @Init
- public void init() {
- Clients.evalJavaScript(String.format("pushNav('%s')", Constants.NAV_EXPORT_DATA));
- onRefresh();
- }
-
- @Command
- public void onRefresh() {
- log.info("Refresh");
- }
-
- @Command
- public void onStartExport() {
- log.info("Starting export");
- scheduledTasks.startExport();
- }
-
- @Override
- public void onApplicationEvent(ExportCompletedEvent event) {
- log.info("Export completed");
- }
-}
@Async
@EventListener
public void handleEvent(ImportStartedEvent evt) {
- log.info("ImportStartedEvent handle");
Optional.ofNullable(startedConsumer).ifPresent(c -> c.accept(evt));
}
@Async
@EventListener
public void handleEvent(ImportCompletedEvent evt) {
- log.info("ImportCompletedEvent handle");
Optional.ofNullable(completedConsumer).ifPresent(c -> c.accept(evt));
}
}
import hu.user.mcvodsync.db.repository.PlaylistSyncRepository;
import hu.user.mcvodsync.db.repository.UploadFileRepository;
import hu.user.mcvodsync.service.config.ScheduledTasks;
+import hu.user.mcvodsync.ui.ApplicationProperties;
import hu.user.mcvodsync.ui.Constants;
import hu.user.mcvodsync.ui.auth.CurrentProfile;
import hu.user.mcvodsync.ui.session.SessionSettings;
@Setter
public class IndexViewModel {
+ @WireVariable
+ private ApplicationProperties applicationProperties;
+
@WireVariable
private BuildProperties buildProperties;
@Command
public void onTimer() {
BindUtils.postNotifyChange(scheduledTasks, "status");
+ BindUtils.postNotifyChange(scheduledTasks, "outOfSync");
}
@Command
public void route(String path) {
if (Constants.NAV_ROOT.equals(path))
- path = Constants.NAV_IMPORT_FILE;
+ path = Constants.NAV_MAIN;
setPage("~." + path + ".zul");
BindUtils.postNotifyChange(this, "page");
}
}
@Command
- public void onReset() {
- cleanupLocal();
+ public void onResetRemote() {
cleanupRemote();
Messagebox.show("Cleanup completed.", "Info", Messagebox.OK, Messagebox.INFORMATION);
}
+ @Command
+ public void onResetLocal() {
+ cleanupLocal();
+ Messagebox.show("Cleanup completed.", "Info", Messagebox.OK, Messagebox.INFORMATION);
+ }
+
private void cleanupLocal() {
log.info("cleanupLocal");
playlistSyncRepository.deleteAllInBatch();
assetSyncRepository.deleteAllInBatch();
assetRepository.deleteAllInBatch();
uploadFileRepository.deleteAllInBatch();
+ scheduledTasks.reset();
log.info("cleanupLocal OK");
}
log.info("cleanupRemote");
cleanupRemotePlaylists();
cleanupRemoteVideos();
+ scheduledTasks.reset();
log.info("cleanupRemote OK");
}
@Getter
@Log4j2
-public class ImportFileViewModel extends FilterActiveViewModel<UploadFile> {
+public class MainViewModel extends FilterActiveViewModel<UploadFile> {
@WireVariable
ImportFileDataModel importFileDataModel;
@Override
protected String getNavigation() {
- return Constants.NAV_IMPORT_FILE;
+ return Constants.NAV_MAIN;
}
@Init
@Command
public void onTimer() {
+ BindUtils.postNotifyChange(scheduledTasks, "exportSummary");
BindUtils.postNotifyChange(scheduledTasks, "importSummary");
}
scheduledTasks.startImport();
}
+ @Command
+ public void onStartExport() {
+ scheduledTasks.startExport();
+ }
+
+
}
<zk>
<config-name/>
<session-config>
- <session-timeout>300</session-timeout>
+ <session-timeout>3600</session-timeout>
<timeout-uri>/timeout</timeout-uri>
</session-config>
<client-config>
<name>org.zkoss.zul.nativebar</name>
<value>true</value>
</library-property>
- <language-addon>
- <component>
- <component-name>menuitem</component-name>
- <extends>menuitem</extends>
- <property>
- <property-name>autodisable</property-name>
- <property-value>self</property-value>
- </property>
- </component>
- </language-addon>
<!-- PROD -->
<!-- <desktop-config>-->
<!-- <file-check-period>600</file-check-period><!– unit: seconds –>-->
<!-- font-weight: bold;-->
<!-- }-->
<!-- </style>-->
- <window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.AssociatesViewModel')">
- <caption label="Munkatársak"/>
+ <window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.AssociatesViewModel')"
+ style="margin: 20px 0 20px 0">
<borderlayout>
<north flex="true">
- <toolbar>
- <toolbarbutton label="Hozzáadás" iconSclass="z-icon-plus" onClick="@command('onAdd')"/>
- <toolbarbutton label="Szerkesztés" iconSclass="z-icon-edit" onClick="@command('onEdit')"
- disabled="@load(empty vm.selectedEntity)"/>
- <toolbarbutton label="Törlés" iconSclass="z-icon-remove" onClick="@command('onDelete')"
- disabled="@load(empty vm.selectedEntity)"/>
- <separator orient="vertical"/>
- <toolbarbutton mode="toggle" iconSclass="z-icon-check" label="Aktív"
- checked="@bind(vm.filterShowActive)"/>
- <toolbarbutton mode="toggle" iconSclass="z-icon-ban" label="Inaktív"
- checked="@bind(vm.filterShowInActive)"/>
- <toolbarbutton mode="toggle" iconSclass="z-icon-adjust" label="Mind"
- checked="@bind(vm.filterShowBoth)"/>
- </toolbar>
+ <vlayout>
+ <hbox pack="start" align="center" hflex="true" vflex="true" style="padding: 10px">
+ <label style="font-size: 1.5em; font-weight:bold; color: gray" value="USERS"/>
+ </hbox>
+ <toolbar>
+ <toolbarbutton label="Add" iconSclass="z-icon-plus" onClick="@command('onAdd')"
+ style="font-size: 1.2em"/>
+ <toolbarbutton label="Edit" iconSclass="z-icon-edit" onClick="@command('onEdit')"
+ disabled="@load(empty vm.selectedEntity)" style="font-size: 1.2em"/>
+ <!-- <toolbarbutton label="Delete" iconSclass="z-icon-remove" onClick="@command('onDelete')"-->
+ <!-- disabled="@load(empty vm.selectedEntity)" style="font-size: 1.2em"/>-->
+ <space bar="true"/>
+ <toolbarbutton mode="toggle" iconSclass="z-icon-check" label="Active"
+ checked="@bind(vm.filterShowActive)" style="font-size: 1.2em"/>
+ <toolbarbutton mode="toggle" iconSclass="z-icon-ban" label="Inactive"
+ checked="@bind(vm.filterShowInActive)" style="font-size: 1.2em"/>
+ <toolbarbutton mode="toggle" iconSclass="z-icon-adjust" label="All"
+ checked="@bind(vm.filterShowBoth)" style="font-size: 1.2em"/>
+ </toolbar>
+ </vlayout>
</north>
<center border="none" flex="true">
<listbox vflex="true" model="@load(vm.associatesDataModel)"
<zk>
- <window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.BrowseRemoteViewModel')">
- <caption label="Browse BrightCove"/>
+ <window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.BrowseRemoteViewModel')"
+ style="margin: 20px 0 20px 0">
<borderlayout>
<north hflex="true">
- <toolbar>
- <toolbarbutton label="Reload" iconSclass="z-icon-retweet" onClick="@command('onRefresh')"/>
- </toolbar>
+ <vlayout>
+ <hbox pack="start" align="center" hflex="true" vflex="true" style="padding: 10px">
+ <label style="font-size: 1.5em; font-weight:bold; color: gray" value="BROWSE BRIGHTCOVE"/>
+ </hbox>
+ <toolbar>
+ <toolbarbutton label="Reload" iconSclass="z-icon-refresh" onClick="@command('onRefresh')"
+ style="font-size: 1.2em"/>
+ </toolbar>
+ </vlayout>
</north>
<center border="none" hflex="true" vflex="true">
<hbox spacing="0" hflex="true" vflex="true">
<listbox hflex="true" vflex="true" model="@load(vm.brightCovePlaylistDataModel)"
autopaging="true" mold="paging" pagingPosition="top" multiple="false"
- selectedItem="@bind(vm.selectedPlaylist)">
+ selectedItem="@bind(vm.selectedPlaylist)" sizedByContent="true" span="true">
<listhead sizable="true">
<listheader label="ID" align="left"/>
<listheader label="Name" align="left"/>
<listheader label="Video count" align="left"/>
+ <listheader label="Description" align="left"/>
</listhead>
<template name="model">
<listitem>
<listcell label="@load(each.id)"/>
<listcell label="@load(each.name)"/>
<listcell label="@load(each.getVideoIds().size())"/>
+ <listcell label="@load(each.description)"/>
</listitem>
</template>
</listbox>
<splitter collapse="before"/>
<listbox hflex="true" vflex="true" model="@load(vm.brightCoveVideoDataModel)"
- autopaging="true" mold="paging" pagingPosition="top" multiple="false">
+ autopaging="true" mold="paging" pagingPosition="top" multiple="false"
+ sizedByContent="true" span="true">
<listhead sizable="true">
<listheader label="ID" align="left"/>
- <listheader label="Catalog ID" align="left"/>
+ <listheader label="Reference ID" align="left"/>
+ <listheader label="Notes" align="left"/>
+ <listheader label="Name" align="left"/>
+ <listheader label="Description" align="left"/>
+ <listheader label="Show" align="left"/>
+ <listheader label="Season" align="left"/>
+ <listheader label="Episode" align="left"/>
+ <listheader label="Production year" align="left"/>
+ <listheader label="Rating TV" align="left"/>
+ <listheader label="Tags" align="left"/>
+ <listheader label="Long description" align="left"/>
</listhead>
<template name="model">
<listitem>
<listcell label="@load(each.id)"/>
<listcell label="@load(each.referenceId)"/>
+ <listcell label="@load(each.customFields['notes'])"/>
+ <listcell label="@load(each.name)"/>
+ <listcell label="@load(each.description)"/>
+ <listcell label="@load(each.customFields['show'])"/>
+ <listcell label="@load(each.customFields['season'])"/>
+ <listcell label="@load(each.customFields['episode'])"/>
+ <listcell label="@load(each.customFields['production_year'])"/>
+ <listcell label="@load(each.customFields['rating_tv'])"/>
+ <listcell label="@load(each.tags)"/>
+ <listcell label="@load(each.longDescription)"/>
</listitem>
</template>
</listbox>
--- /dev/null
+<zk>
+ <style>
+ .bold-font {
+ font-weight: bold;
+ font-size: 1.3em;
+ }
+ </style>
+ <vlayout>
+ <grid span="true">
+ <columns>
+ <column hflex="min"/>
+ <column hflex="1"/>
+ <column hflex="1"/>
+ <column hflex="1"/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="left" valign="bottom">
+ <label value="Started:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.startedFormatted)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Finished:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.finishedFormatted)"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+ <label value="Video statistics:"/>
+ <grid span="true">
+ <columns>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="right" valign="bottom">
+ <label value="All:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.video.all)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Success:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.video.success)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Error:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.video.error)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Skipped:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.video.skip)"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+ <grid span="true">
+ <columns>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="right" valign="bottom">
+ <label value="Inserted:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.video.insert)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Updated:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.video.update)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Deleted:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.video.delete)"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+ <label value="Playlist statistics:"/>
+ <grid span="true">
+ <columns>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="right" valign="bottom">
+ <label value="All:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.playlist.all)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Success:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.playlist.success)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Error:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.playlist.error)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Skipped:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.playlist.skip)"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+ <grid span="true">
+ <columns>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="right" valign="bottom">
+ <label value="Inserted:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.playlist.insert)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Updated:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.playlist.update)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Deleted:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.exportSummary.playlist.delete)"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+
+ </vlayout>
+</zk>
\ No newline at end of file
--- /dev/null
+<zk>
+ <style>
+ .bold-font {
+ font-weight: bold;
+ font-size: 1.3em;
+ }
+ </style>
+ <vlayout>
+ <grid span="true">
+ <columns>
+ <column hflex="min"/>
+ <column hflex="1"/>
+ <column hflex="1"/>
+ <column hflex="1"/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="left" valign="bottom">
+ <label value="Started:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.startedFormatted)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Finished:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.finishedFormatted)"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+ <label value="Video statistics:"/>
+ <grid span="true">
+ <columns>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="right" valign="bottom">
+ <label value="All:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.video.all)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Success:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.video.success)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Error:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.video.error)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Skipped:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.video.skip)"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+ <grid span="true">
+ <columns>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="right" valign="bottom">
+ <label value="Inserted:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.video.insert)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Updated:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.video.update)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Deleted:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.video.delete)"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+ <label value="Playlist statistics:"/>
+ <grid span="true">
+ <columns>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="right" valign="bottom">
+ <label value="All:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.playlist.all)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Success:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.playlist.success)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Error:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.playlist.error)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Skipped:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.playlist.skip)"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+ <grid span="true">
+ <columns>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="right" valign="bottom">
+ <label value="Inserted:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.playlist.insert)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Updated:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.playlist.update)"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Deleted:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label sclass="bold-font" value="@load(vm.scheduledTasks.importSummary.playlist.delete)"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+ </vlayout>
+</zk>
\ No newline at end of file
--- /dev/null
+<zk>
+ <label value="ANYÁD"/>
+</zk>
\ No newline at end of file
+++ /dev/null
-<zk>
- <window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.ImportFileViewModel')">
- <timer delay="2000" onTimer="@command('onTimer')" repeats="true"/>
- <caption label="Import file"/>
- <borderlayout>
- <north hflex="true">
- <toolbar>
- <toolbarbutton label="Upload" iconSclass="z-icon-plus" upload="true"
- onUpload="@command('onUploadFile')"/>
- <!-- <toolbarbutton label="Törlés" iconSclass="z-icon-remove" onClick="@command('onDelete')"-->
- <!-- disabled="@load(empty vm.selectedEntity)"/>-->
- </toolbar>
- </north>
- <center border="none" hflex="true" vflex="true">
-
- <hbox spacing="0" hflex="true" vflex="true">
- <listbox id="partnersList" hflex="2" vflex="true" model="@load(vm.importFileDataModel)"
- autopaging="true" mold="paging" pagingPosition="top" multiple="false"
- onSelect="@command('onListSelection')" onDoubleClick="@command('onEdit')">
- <listhead sizable="true">
- <listheader label="Created" sort="auto(created)" align="left"
- sortDirection="@load(vm.cols['created'].sortDirection)"/>
- <listheader label="Name" sort="auto(name)" align="left"
- sortDirection="@load(vm.cols['name'].sortDirection)"/>
- <listheader label="Size" sort="auto(size)" align="left"
- sortDirection="@load(vm.cols['size'].sortDirection)"/>
- </listhead>
- <template name="model">
- <listitem>
- <listcell label="@load(each.created)"/>
- <listcell label="@load(each.name)"/>
- <listcell label="@load(each.size)"/>
- </listitem>
- </template>
- </listbox>
- <splitter collapse="before"/>
- <window title="Last import statistics" hflex="true" vflex="true">
- <vlayout>
- <grid span="true">
- <columns>
- <column hflex="min"/>
- <column hflex="1"/>
- <column hflex="min"/>
- <column hflex="1"/>
- </columns>
- <rows>
- <row>
- <cell align="left" valign="bottom">
- <label value="Started:"/>
- </cell>
- <cell align="center" valign="middle">
- <label value="@load(vm.scheduledTasks.importSummary.started)"
- sclass="hightlight"/>
- </cell>
- <cell align="left" valign="bottom">
- <label value="Finished:"/>
- </cell>
- <cell align="center" valign="middle">
- <label value="@load(vm.scheduledTasks.importSummary.finished)"
- sclass="hightlight"/>
- </cell>
- </row>
- </rows>
- </grid>
- <grid span="true">
- <columns>
- <column/>
- <column/>
- <column/>
- <column/>
- <column/>
- <column/>
- </columns>
- <rows>
- <row>
- <cell align="right" valign="bottom">
- <label value="All:"/>
- </cell>
- <cell align="left" valign="middle">
- <label value="@load(vm.scheduledTasks.importSummary.all)"
- sclass="hightlight"/>
- </cell>
- <cell align="right" valign="bottom">
- <label value="Success:"/>
- </cell>
- <cell align="left" valign="middle">
- <label value="@load(vm.scheduledTasks.importSummary.success)"
- sclass="hightlight"/>
- </cell>
- <cell align="right" valign="bottom">
- <label value="Error:"/>
- </cell>
- <cell align="left" valign="middle">
- <label value="@load(vm.scheduledTasks.importSummary.error)"
- sclass="hightlight"/>
- </cell>
- </row>
- <row>
- <cell align="right" valign="bottom">
- <label value="Inserted:"/>
- </cell>
- <cell align="left" valign="middle">
- <label value="@load(vm.scheduledTasks.importSummary.inserted)"
- sclass="hightlight"/>
- </cell>
- <cell align="right" valign="bottom">
- <label value="Updated:"/>
- </cell>
- <cell align="left" valign="middle">
- <label value="@load(vm.scheduledTasks.importSummary.updated)"
- sclass="hightlight"/>
- </cell>
- <cell align="right" valign="bottom">
- <label value="Deleted:"/>
- </cell>
- <cell align="left" valign="middle">
- <label value="@load(vm.scheduledTasks.importSummary.deleted)"
- sclass="hightlight"/>
- </cell>
- </row>
- </rows>
- </grid>
-
- </vlayout>
- </window>
- </hbox>
-
- </center>
- </borderlayout>
- </window>
-</zk>
\ No newline at end of file
}
</script>
<window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.IndexViewModel')">
- <timer delay="2000" onTimer="@command('onTimer')" repeats="true"/>
+ <timer delay="1000" onTimer="@command('onTimer')" repeats="true"/>
<caption sclass="header" id="appHeader">
<div style="display: block">
<div style="display: inline; float: left">
<vlayout>
<hlayout valign="middle">
<menubar autodrop="true" hflex="true">
- <menuitem iconSclass="z-icon-cloud-download" label="Import file"
- onClick="@command(vm.route(Constants.NAV_IMPORT_FILE))"/>
- <menuitem iconSclass="z-icon-cloud-upload" label="Export data"
- onClick="@command(vm.route(Constants.NAV_EXPORT_DATA))"/>
+ <menuitem iconSclass="z-icon-retweet" label="Synchronization"
+ onClick="@command(vm.route(Constants.NAV_MAIN))"/>
<menuitem image="~./static/images/bc.ico" label="Browse BrightCove"
onClick="@command(vm.route(Constants.NAV_BROWSE_REMOTE))"/>
- <menuseparator/>
+ <menuseparator visible="@load(vm.applicationProperties.admin)"/>
<menuitem iconSclass="z-icon-user" label="Users"
- onClick="@command(vm.route(Constants.NAV_ASSOCIATES))"/>
- <menuitem iconSclass="z-icon-trash" label="Reset data"
- onClick="@command('onReset')"/>
+ onClick="@command(vm.route(Constants.NAV_ASSOCIATES))"
+ visible="@load(vm.applicationProperties.admin)"/>
+ <menuitem iconSclass="z-icon-trash" label="Reset local data"
+ onClick="@command('onResetLocal')"
+ visible="@load(vm.applicationProperties.admin)"/>
+ <menuitem iconSclass="z-icon-trash" label="Reset remote data"
+ onClick="@command('onResetRemote')"
+ visible="@load(vm.applicationProperties.admin)"/>
</menubar>
<hbox hflex="min" pack="right">
</hbox>
<center border="none" hflex="true">
<include src="@load(vm.page)" hflex="true" vflex="true"/>
</center>
- <south height="50px">
- <hbox pack="end" align="center" hflex="true" vflex="true">
- <label style="font-size: 1.5em; padding-right: 20px; color: navy"
- value="@load(vm.scheduledTasks.status)"/>
- </hbox>
+ <south>
+ <hlayout>
+ <hbox pack="start" align="center" hflex="true" vflex="true">
+ <label style="font-size: 1.5em; font-weight: bold; padding: 20px; color: red"
+ value="Out of sync!" visible="@load(vm.scheduledTasks.outOfSync)"/>
+ </hbox>
+ <hbox pack="end" align="center" hflex="true" vflex="true">
+ <label style="font-size: 1.5em; padding: 20px; color: navy"
+ value="@load(vm.scheduledTasks.status)"/>
+ </hbox>
+ </hlayout>
</south>
</borderlayout>
</window>
--- /dev/null
+<zk>
+ <window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.MainViewModel')"
+ style="margin: 20px 0 20px 0">
+ <timer delay="1000" onTimer="@command('onTimer')" repeats="true"/>
+ <borderlayout>
+ <north hflex="true">
+ <vlayout>
+ <hbox pack="start" align="center" hflex="true" vflex="true" style="padding: 10px">
+ <label style="font-size: 1.5em; font-weight:bold; color: gray" value="SYNCHRONIZATION"/>
+ </hbox>
+ <toolbar>
+ <toolbarbutton label="Import" iconSclass="z-icon-cloud-download" upload="true"
+ onUpload="@command('onUploadFile')" style="font-size: 1.2em"/>
+ <!-- <space bar="true"/>-->
+ <separator orient="vertical"/>
+ <toolbarbutton label="Export" iconSclass="z-icon-cloud-upload"
+ onClick="@command('onStartExport')" style="font-size: 1.2em"/>
+ </toolbar>
+ </vlayout>
+ </north>
+ <center border="none" hflex="true" vflex="true">
+
+ <hbox spacing="0" hflex="true" vflex="true">
+ <listbox id="partnersList" hflex="2" vflex="true" model="@load(vm.importFileDataModel)"
+ autopaging="true" mold="paging" pagingPosition="top" multiple="false"
+ onSelect="@command('onListSelection')" onDoubleClick="@command('onEdit')">
+ <listhead sizable="true">
+ <listheader label="Created" sort="auto(created)" align="left"
+ sortDirection="@load(vm.cols['created'].sortDirection)"/>
+ <listheader label="Name" sort="auto(name)" align="left"
+ sortDirection="@load(vm.cols['name'].sortDirection)"/>
+ <listheader label="Size" sort="auto(size)" align="left"
+ sortDirection="@load(vm.cols['size'].sortDirection)"/>
+ </listhead>
+ <template name="model">
+ <listitem>
+ <listcell label="@load(each.created)"/>
+ <listcell label="@load(each.name)"/>
+ <listcell label="@load(each.size)"/>
+ </listitem>
+ </template>
+ </listbox>
+ <splitter/>
+ <div hflex="true" vflex="true" style="overflow: auto">
+ <vbox spacing="0" hflex="true" vflex="true">
+ <window title="Last import" hflex="true" style="margin: 10px" border="normal"
+ visible="@load(not empty vm.scheduledTasks.importSummary)">
+ <include src="~./form/import-stats.zul"/>
+ </window>
+ <window title="Last export" hflex="true" style="margin: 10px" border="normal"
+ visible="@load(not empty vm.scheduledTasks.exportSummary)">
+ <include src="~./form/export-stats.zul"/>
+ </window>
+ </vbox>
+ </div>
+
+ </hbox>
+
+ </center>
+ </borderlayout>
+ </window>
+</zk>
\ No newline at end of file